Explain Codes LogoExplain Codes Logo

Mocking member variables of a class using Mockito

java
mockito
dependency-injection
testing
Anton ShumikhinbyAnton Shumikhin·Feb 28, 2025
TLDR

Use Mockito's @InjectMocks to instantiate your class under test, and @Mock to create mock objects for its dependencies. Through these annotations, Mockito executes the injection process automatically. Here's how you can do it:

public class Spaceship { private NavigationSystem navSystem; // Our spaceship's built-in navigation system // ... journey to the stars using 'navSystem' ... } public class SpaceshipTest { @Mock NavigationSystem mockNavSystem; // Fake it till you make it to Mars! @InjectMocks Spaceship spaceship; // Trust me, I'm a mock engineer // ... run tests with 'mockNavSystem' working within 'spaceship' ... }

By annotating Spaceship with @InjectMocks and NavigationSystem with @Mock, Mockito injects the mockNavSystem into spaceship, enabling easy testing of spaceship functions.

Refactoring code: making it easy to test

When using Mockito, your code needs to be structured with dependency injections in mind, so mocking becomes easier. If your member variables aren't readily accessible, I bulletproof suggestions:

  • Add setter methods so that injecting mocks becomes straightforward.
  • Modify the constructor, so dependencies are variables instead of constants.

Unreachable members: using reflection

If you can't refactor your code or your member variables are inaccessible, ReflectionTestUtils from the Spring framework to the rescue:

ReflectionTestUtils.setField(spaceship, "navSystem", mockNavSystem);

You can also concoct a custom TestUtils class equipped with reflection methods for setting private variables. Be warned though, over-reliance on reflection screams the need for refactoring.

Over-mocking: the danger zone

Cross the line and mock what's not yours. Over-mocking transforms into fragile tests that fail to validate the interaction between components. Prefer using authentic instances of helper entities or data objects where applicable.

Mocking best practices: secure, safe, and sound!

Dependency injection isn't just a testing trick; it's a philosophy in software design. Vetted use of it ensures that each class sticks to its core duties without worrying about creating its dependencies.

Design patterns: adding structure

Implementing concepts like Factory, Strategy, or Service Locator patterns embeds structure into your code, providing a clear route for mocking and testing.

Ensuring correct usage of mocks

Leverage Mockito's verification capabilities to confirm the right use of your mock within your class:

verify(mockNavSystem).expectedCourseCorrection(); // Confirming if we got the directions right!

Dealing with legacy code

Legacy codebases may resist refactoring. Here, you can rely on the reflection capabilities to offset the issues temporarily:

Field field = Spaceship.class.getDeclaredField("navSystem"); field.setAccessible(true); field.set(spaceship, mockNavSystem); // Now we are talking private!

Reflection drawbacks

Remember, the extra power of reflection can lead to fragile testing. Such tests are prone to break after refactoring the field's names or types, as they tightly bind the tests to specific implementation details.