Junior
What is a mock in testing?
sobes.tech AI
Answer from AI
Mock object (mock) is a simulation of a real object or dependency in unit testing. It is used to isolate the tested component by replacing its dependencies with controlled stubs.
Main goals of using mocks:
- Isolation: Testing only a specific class or method without considering the behavior of its dependencies.
- Control: Ability to set expected behavior of dependencies (return values, thrown exceptions, call counts) in various test scenarios.
- Speed: Avoiding execution of resource-intensive or slow operations of real dependencies (e.g., database access, network calls).
- Testing exceptional situations: Easy simulation of errors or non-standard behavior of dependencies, which are hard to reproduce with real objects.
Types of "doubles" in testing (stubs, mocks, etc.):
- Dummy: Passed as arguments but not used.
- Fake: Has a working implementation but simplified (e.g., in-memory database).
- Stub: Provides predefined responses to method calls but does not verify interactions.
- Spy: Essentially a stub that also records information about method calls (how many times, with what arguments).
- Mock: Predefines expected behavior (expected method calls with expected arguments) and performs verification of this behavior at the end of the test.
Libraries for creating mocks in Java:
- Mockito
- EasyMock
- PowerMock (for static, private methods, constructors)
Example with Mockito:
// Class we want to test
class MyService {
private Dependency dependency;
public MyService(Dependency dependency) {
this.dependency = dependency;
}
public String processData(String data) {
String processed = dependency.process(data);
if (processed != null && processed.startsWith("OK")) {
return "Success: " + processed;
} else {
return "Failure";
}
}
}
// Dependency to be mocked
interface Dependency {
String process(String input);
}
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
class MyServiceTest {
@Test
void testProcessDataSuccess() {
// Create mock object
Dependency mockDependency = mock(Dependency.class);
// Set expected behavior of mock: return "OK: data" when process("input")
when(mockDependency.process("input")).thenReturn("OK: data");
// Create the object under test with mock dependency
MyService myService = new MyService(mockDependency);
// Call the method under test
String result = myService.processData("input");
// Check the result
assertEquals("Success: OK: data", result);
// Verify that process("input") was called exactly once
verify(mockDependency, times(1)).process("input");
}
@Test
void testProcessDataFailure() {
// Create mock object
Dependency mockDependency = mock(Dependency.class);
// Set expected behavior: return "Error" when process("input")
when(mockDependency.process("input")).thenReturn("Error");
// Create object under test
MyService myService = new MyService(mockDependency);
// Call method
String result = myService.processData("input");
// Check result
assertEquals("Failure", result);
// Verify call
verify(mockDependency, times(1)).process("input");
}
}
Mocks allow creating predictable and controllable test scenarios, which is critical for reliable unit testing.