Why advanced mocking handles complex dependencies in JUnit - Automation Benefits in Action
import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) class ComplexServiceTest { @Mock DependencyServiceA serviceA; @Mock DependencyServiceB serviceB; @InjectMocks ComplexService complexService; @Test void testComplexServiceMethod() { // Arrange: stub dependent services when(serviceA.getData()).thenReturn("dataA"); when(serviceB.process("dataA")).thenReturn("processedDataB"); // Act: call method under test String result = complexService.combineData(); // Assert: verify result and interactions assertEquals("Result: processedDataB", result); verify(serviceA).getData(); verify(serviceB).process("dataA"); } } // Supporting classes for context class ComplexService { private final DependencyServiceA serviceA; private final DependencyServiceB serviceB; public ComplexService(DependencyServiceA serviceA, DependencyServiceB serviceB) { this.serviceA = serviceA; this.serviceB = serviceB; } public String combineData() { String data = serviceA.getData(); String processed = serviceB.process(data); return "Result: " + processed; } } interface DependencyServiceA { String getData(); } interface DependencyServiceB { String process(String input); }
This test uses Mockito with JUnit 5 to handle complex dependencies.
We use @Mock to create mocks for the two dependent services. @InjectMocks injects these mocks into the service under test.
We stub the dependent services' methods to return controlled values using when().thenReturn(). This simulates complex interactions without needing real implementations.
The test calls the service method and asserts the returned value matches the expected result based on the mocks.
Finally, verify() checks that the dependent services were called correctly, ensuring the service interacts properly with its dependencies.
This approach isolates the service logic and handles complex dependencies cleanly and reliably.
Now add data-driven testing with 3 different input scenarios for the dependent services
Practice
Solution
Step 1: Understand the purpose of mocking
Mocking replaces real dependencies with fake ones to isolate the code being tested.Step 2: Recognize the benefit of advanced mocking
Advanced mocking allows control over complex dependencies, making tests reliable and focused.Final Answer:
To isolate the code under test and control the behavior of its dependencies -> Option BQuick Check:
Mocking isolates dependencies [OK]
- Thinking mocking speeds up production code
- Believing mocking removes the need for tests
- Confusing mocking with UI generation
Solution
Step 1: Recall Mockito mock creation syntax
Mockito uses the static method mock(Class) to create mock objects.Step 2: Verify the correct syntax
The correct syntax is: MyClass mock = Mockito.mock(MyClass.class);Final Answer:
MyClass mock = Mockito.mock(MyClass.class); -> Option AQuick Check:
Mockito.mock(Class) creates mocks [OK]
- Using new keyword instead of Mockito.mock
- Calling non-existent Mockito methods
- Incorrect class reference syntax
@ExtendWith(MockitoExtension.class)
class ServiceTest {
@Mock
Repository repo;
@InjectMocks
Service service;
@Test
void testGetData() {
when(repo.fetch()).thenReturn("data");
String result = service.getData();
assertEquals("data", result);
}
}
What will be the test result if
repo.fetch() is called inside service.getData()?Solution
Step 1: Understand Mockito annotations usage
@Mock creates a mock for Repository, and @InjectMocks injects it into Service.Step 2: Analyze the when-thenReturn setup
Calling repo.fetch() returns "data" as stubbed, so service.getData() returns "data".Final Answer:
Test passes because repo.fetch() returns mocked "data" -> Option DQuick Check:
Mock returns stubbed value [OK]
- Assuming mocks return null by default
- Forgetting to initialize mocks with @ExtendWith or MockitoAnnotations
- Confusing mock injection with real object creation
@ExtendWith(MockitoExtension.class)
class TestClass {
@Mock
Database db;
@Test
void testQuery() {
when(db.query("SELECT * FROM users")).thenReturn(null);
String result = db.query("SELECT * FROM users");
assertNotNull(result);
}
}
What is the problem with this test?
Solution
Step 1: Analyze the stubbed return value
The mock is set to return null for the query method call.Step 2: Check the assertion logic
The test asserts that the result is not null, but the stub returns null, so assertion fails.Final Answer:
The assertion will fail because the mock returns null -> Option AQuick Check:
Assertion contradicts stubbed null [OK]
- Ignoring that stub returns null
- Assuming syntax error without checking code
- Believing test passes with failing assertions
Solution
Step 1: Identify the need to isolate service from external APIs
Testing should avoid calling real APIs to keep tests fast and reliable.Step 2: Choose advanced mocking to simulate API behavior
Mockito mocks for API clients allow controlling responses and injecting them into the service.Final Answer:
Use Mockito to create mocks for both API clients and inject them into the service -> Option CQuick Check:
Mock external APIs to isolate service [OK]
- Testing with real APIs causing slow or flaky tests
- Hardcoding fake responses inside service code
- Avoiding tests due to complexity
Ready to prove it?
Pass the test and this pattern moves unknown
