Explain Codes LogoExplain Codes Logo

Mockito: how to verify method was called on an object created within a method?

java
mocking
test-driven-development
dependency-injection
Anton ShumikhinbyAnton Shumikhin·Aug 20, 2024
TLDR

To verify method calls on objects instantiated within a method, use ArgumentCaptor or spy features from Mockito.

Example with ArgumentCaptor:

//Catch 'em all - Pokemon style! ArgumentCaptor<MyObject> captor = ArgumentCaptor.forClass(MyObject.class); verify(mockedCollaborator).method(captor.capture()); //Verify captured object like a detective cracked the code! verify(captor.getValue()).desiredInteraction();

Example with spy:

//Here comes the spy! Remember - "I Spy with my little eye" game? MyObject spyObject = spy(new MyObject()); whenNew(MyObject.class).thenReturn(spyObject); //A successful spy always verifies its mission! verify(spyObject).desiredInteraction();

Master the art of method verification in encapsulated objects

Let's dive deeper into techniques for method verification in encapsulated objects, addressing complexities, resiliency, and sustainability in test design.

Unleashing the power of Factory-pattern and Dependency Injection

Enlighten your Foo class with a BarFactory and enjoy the benefits of Dependency Injection (DI). This will allow you to inject a mock factory during tests and effortlessly verify its interactions.

class Foo { private BarFactory barFactory; public Foo(BarFactory barFactory) { this.barFactory = barFactory; } public void method() { //All great things come from factories, even Bars Bar bar = barFactory.create(); // ... use the bar, and yes, responsibly!... } }

During the test, send in a mock BarFactory, catching and verifying the Bar instance.

Bar mockBar = mock(Bar.class); //Salute the mockBar! BarFactory mockFactory = () -> mockBar; Foo foo = new Foo(mockFactory); //foo and bar, besties forever! foo.method(); //In mocks we trust! verify(mockBar, times(1)).someMethod();

Sailing with Test-Driven Mocking Strategies

By applying Test-Driven Development (TDD), our design remains clean and testable from conception. Mock dependencies through constructor, setter, or field injection using @InjectMocks to focus on the external behaviour of our class under test, not on how it creates its dependencies.

PowerMockito: When Sherlock meets Harry (The Wizarding world of mocks!)

With PowerMockito, you can actually mock constructor calls with the help of whenNew method, coupled with @PrepareForTest annotation. This is handy when you are stuck with legacy codes where refactoring could open Pandora's box!

@RunWith(PowerMockRunner.class) @PrepareForTest(Foo.class) public class FooTest { @Test public void testMethod() throws Exception { MyObject myObject = mock(MyObject.class); //Magic spell to mock constructor calls PowerMockito.whenNew(MyObject.class).withAnyArguments().thenReturn(myObject); // Execute method that creates a new MyObject internally // Abracadabra, let's create an instance! new Foo().method(); //Unleash the Sherlock in you! verify(myObject).desiredInteraction(); } }

MockitoJUnitRunner: The Mocks Marathon

Bag a smooth ride on MockitoJUnitRunner train, that saves you many initialization boilerplates. It auto-initializes your mocks and injects them into the class under test.

@RunWith(MockitoJUnitRunner.class) public class FooTest { @Mock private BarFactory barFactory; @Mock private Bar bar; @InjectMocks private Foo foo; @Test public void testMethod() { //It's mock'o'clock when(barFactory.create()).thenReturn(bar); foo.method(); //Trust but verify. - Ronald Reagan verify(bar, times(1)).someMethod(); } }

Best practices and future-proof strategies

Let's explore some of the contemporary practices and principles of method verification.

Designing for Testability

When it comes to design, testability is everything! Consider constructor, setter, and field injection to make your classes more flexible and easily testable.

Behaviour over Implementation

In testing, always target to verify the expected behaviour rather than poking around the internal workings. This approach often results in more resilient tests.

The Broader Picture

While verifying individual methods is great, also consider the overall impact on linked objects and system. As such, design your tests to assert these broader outcomes.

Knowing is Winning

Stay informed! Refer to official Mockito documentation and regularly engage with community-driven platforms to stay updated on fresher techniques and strategies.