In unit testing, mocking and spying are two important techniques for isolating a class or component’s behavior. Both are part of the Mockito framework, a widely used Java library for creating mock objects and performing unit tests.
Mocking allows the user to create a simulated object that copies the behavior of a real one but is entirely controlled by the user in the test. Spying allows wrapping an actual object to mock specific methods or behaviors while still allowing the real object to function for other methods.
Read this article to learn more about Mockito Mock and Spy, including their differences, use, and working patterns.
Mockito Mock vs Spy: Key Differences
Here are some of the major differences between Mockito Mock and Spy:
Aspect | Mockito Mock | Mockito Spy |
---|---|---|
Definition | Creates a mock object of a class, which does not have real behavior unless explicitly defined. | Wraps an existing object, allowing both real and mocked behavior. |
Behavior | Analyzes the behavior of a class. All methods return default values unless specified. | Executes real methods unless explicitly mocked. |
Use Case | Ideal for isolating tests by managing dependencies. | Ideal for testing partial behaviors where some methods are real, and others are mocked. |
Stubbing | Always need explicit stubbing for method behavior. | Allows partial stubbing along with real method calls. |
Object Type | Can mock both interfaces and concrete classes. | Can spy on both interfaces and concrete classes. |
Default Behavior | Returns default values like null, 0, or false unless specified. | Executes the real method unless explicitly mocked. |
Created by | Created using Mockito.mock(Class<T>.class). | Created using Mockito.spy(Object). |
What is Mockito Mock?
A Mockito Mock is an object created by the Mockito framework that analyzes the behavior of a real object or class. By using mocks, any user can control what methods return and verify interactions with these methods, isolating unit tests from external dependencies.
Syntax and Real-World Example
Here’s an example of how to mock an object using Mockito:
import static org.mockito.Mockito.*; public class UserServiceTest { @Test public void testGetUser() { // Create a mock of the UserRepository class UserRepository userRepository = mock(UserRepository.class); when(userRepository.getUserById(1)).thenReturn(new User(1, "Ayush Singh")); UserService userService = new UserService(userRepository); User user = userService.getUser(1); assertEquals("Ayush Singh", user.getName()); } }
Learn More: What is Android Unit Testing?
Importance of Mockito Mock
Below are the reasons why Mocking is important:
- Isolation: Mocks help isolate the tested unit by replacing real dependencies with controlled mock objects.
- Behavior control: Mocks allow the specification of exact behavior, such as method return values, exceptions, or interactions.
- Test speed: Mocks speed up unit tests since they do not involve expensive operations, like database calls or HTTP requests.
Read More: Top 15 Unit Testing Tools
How does Mockito Mock work?
Mockito mocks a class or interface by creating a fake version that a user can control. When a method is called on a mock object, it returns default values unless explicitly told to return something else using when(…).thenReturn(…).
How to Use Mock – Steps
Step 1: Create the mock object by using Mockito.mock(Class<T>.class).
Step 2: Use when(…).thenReturn(…) to specify what the mock should return.
Step 3: Call methods on the mock object in the test.
Step 4: Use verify(mock).methodCall() to confirm that methods were invoked.
What is Mockito Spy?
A Mockito Spy allows the user to wrap an existing object (real object) to spy on its behavior. It lets users mock certain methods while leaving the real methods untouched.
This is useful when users want to test real behavior but still mock certain methods or interactions.
Syntax and Real-World Example
Here’s the basic syntax along with an example to create a spy in Mockito:
import static org.mockito.Mockito.*; public class UserServiceTest { @Test public void testSaveUser() { UserRepository realUserRepository = new UserRepository(); UserRepository spyRepository = spy(realUserRepository); doReturn(false).when(spyRepository).save(any(User.class)); // Test the method UserService userService = new UserService(spyRepository); boolean result = userService.saveUser(new User(1, "Ayush Singh")); assertFalse(result); } }
Importance of Mockito Spy
Here are the reasons why Mockito Spy is important:
- Partial mocking: Spy allows users to mock specific methods while keeping the real behavior of others.
- Verification of real interactions: Spying is ideal when any user wants to verify real method calls in an object, especially when those methods are part of a larger, more complex operation.
Learn More: Unit Testing in Java with JUnit
How does Spy work?
Mockito spies on an existing object, allowing the real method to be executed unless explicitly mocked.
Unlike mocks, spies don’t automatically return default values; they pass on to the real method unless told otherwise.
How to Use Mockito Spy
Below are the steps on how you can start using Mockito Spy:
Step 1: Create the real object on which spy needs to be implemented.
Step 2: Use Mockito.spy() to wrap the real object.
Step 3: Use doReturn(…).when(…) to mock specific methods.
Step 4: Use verify(spy).methodCall() to verify interactions.
What is the difference between Spy and Mock?
Here is an explanation of the difference between Mockito Spy and Mockito Mock with the use of an example.
Example:
import static org.mockito.Mockito.*; public class ServiceTest { @Test public void testSpyVsMock() { // Using Mock List<String> mockList = mock(List.class); when(mockList.size()).thenReturn(5); assertEquals(5, mockList.size()); // Using Spy List<String> realList = new ArrayList<>(); List<String> spyList = spy(realList); when(spyList.size()).thenReturn(5); assertEquals(5, spyList.size()); spyList.add("Item"); verify(spyList).add("Item"); } }
Output:
- The mock object will return 5 for the size() method regardless of the actual data in the list.
- The spy object will return the mocked value 5, but a user can verify real interactions, like adding () to the list.
Mockito Mock vs Spy: How to choose?
Mockito provides Mock and Spy to help isolate dependencies in unit testing, but choosing the right approach depends on the testing scenario.
While Mock replaces the entire object with a dummy implementation, Spy allows partial mocking, letting some real methods execute.
Here’s how to decide which one to use:
When to Use Mock:
- Ideal for isolating the unit under test without executing real code.
- Useful when the object has external dependencies (e.g., database calls, API requests).
- Best suited for testing interactions rather than actual method execution.
When to Use Spy:
- Allows testing real behavior while mocking specific methods.
- Useful when only certain methods need to be overridden, while others retain their actual implementation.
- Helps verify interactions, such as tracking calls to real methods like add().
Common Challenges of Using Mockito Mock
Below is a list of some common limitations of using Mockito Mock:
- Complex mocks: Creating mocks for classes with simple behavior can be difficult.
- Overuse of mocks: Too many mocks can lead to flexible tests that are majorly connected to the implementation.
- Stubbing difficulties: Mocks may need extensive setup to specify all method return values.
Best Practices of Using Mockito Mock
Here are some key best practices of using Mockit Mock:
- Keep mocks simple and avoid unnecessary complexity.
- Use mocks mainly to isolate the system under test from external dependencies.
- Focus on verifying interactions with mocks to ensure methods are called as expected.
Common Challenges of Using Mockito Spy
Here are some common challenges of using Mockito Spy:
- Real method execution: Spy executes real methods by default, which may cause unintended side effects.
- Limited mocking: Unlike mocks, spies require explicit stubbing to override real method behavior.
- Increased test complexity: Partial mocking can make tests harder to maintain and understand, leading to confusion.
- Potential performance issues: Since real methods are executed, tests may run slower than pure mocks.
Best Practices of Using Mockito Spy
Below are some best practices of using Mockito Spy:
- Use spy selectively for cases where partial mocking is necessary.
- Avoid unnecessary spying to prevent unintended real-method executions that may cause side effects.
- Stub only required methods to maintain test reliability and avoid executing logic that isn’t needed for the test.
- Combine with verification to ensure expected interactions with both real and mocked methods.
Conclusion
Both techniques serve different purposes, with Mocks providing complete isolation of the unit under test and Spies allowing partial mocking while still retaining real behavior.
When choosing between Mock and Spy, it’s important to evaluate the complexity of the test, the behavior to isolate, and the interactions to verify.
Tools like BrowserStack Automate are mainly used for real-world testing environments. They allow the user to run the tests on real devices, ensuring that the app behaves as expected across different platforms and browsers. This provides an easy way to confirm that the code works well beyond local tests and mocks.