How to Mock Classes With Constructor Injection

How to mock classes with constructor injection

You want to test someMethod() of class A. Testing the execute() of class B should take place in the other test, because instance of B is a dependency in your case. Test for execute() should be made in different test.

You don't need to test how B object will behave, so you need to mock it and afterwards, check that execute() was invoked.

So in your case your test will look something like this:

  B b = Mockito.mock(B.class);
A a = new A( b );
a.someMethod();
Mockito.verify( b, Mockito.times( 1 ) ).execute();

Constructor injection with mocked object which has to set mocked value in constructor

The below code could solve what you are trying to do (Edit : Added both a test case that uses @SrringBootTest and without it as well)

If not using SpringBoot
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {FooB.class, FooTest.TestConfig.class})
class FooTest {

@Autowired
private FooB fooB;

@TestConfiguration
public static class TestConfig {

@Bean
FooA getFooA(){
FooA fooA = Mockito.mock(FooA.class);
when(fooA.getFoo()).thenReturn("foo from test");
return fooA;
}

}

@Test
void testFooB() {
assertEquals("foo from test", fooB.getFoo());
}
}
If using SpringBoot
@SpringBootTest
class FooTest {

@Autowired
private FooB fooB;

@TestConfiguration
public static class TestConfig {

@MockBean
private FooA fooA;

@PostConstruct
void initMocks() {
when(fooA.getFoo()).thenReturn("foo from test");
}

}

@Test
void testFooB() {
assertEquals("foo from test", fooB.getFoo());
}
}

Mockito injection not working for constructor AND setter mocks together

Yes, the @InjectMocks annotation makes Mockito EITHER do constructor injection, OR setter/field injection, but NEVER both. The rules around which will be chosen are quite complicated, which is one reason why I try to avoid using @InjectMocks whenever possible.

To summarise, Mockito FIRST chooses one constructor from among those that the class has, THEN analyses whether that constructor can be used for constructor injection. The one it chooses will always be the one with the greatest number of arguments. If there are several constructors with the same number of arguments, then it is undefined which one will be chosen.

Constructor injection will NOT be used if the type of one or more of the parameters of the CHOSEN constructor is a primitive type, or a final class or a private class. Even if there are other constructors that COULD be used.

If constructor injection is not used, or if the only constructor is the default constructor, then setter/field injection will be used instead. But setter/field injection is never used in conjunction with constructor injection.

How to inject fields using Mockito without specifying them in constructor?

@InjectMocks will only do one of constructor injection or property injection, not both.

Mockito will try to inject mocks only either by constructor injection,
setter injection, or property injection in order...

You can always do

@Before
public void setUp() {
setter.setPriceTable(priceTable);
}

Or however your table should get wired. However, the cleanest design is typically to unify your dependency injection method to inject everything into the constructor. Since @InjectMocks will choose the biggest constructor and work on private or package-private constructors, one option would be to add a constructor overload:

class PriceSetter {
private Table priceTable;

public PriceSetter(Dependency d1, Dependency d2) {
this(d1, d2, new DefaultPriceTable());
}

PriceSetter(Dependency d1, Dependency d2, Table priceTable) {
this.d1 = d1;
this.d2 = d2;
this.priceTable = priceTable;
}

}

Mocking a spring class with constructor injection

If you create a mock of a class there won't be any dependencies in it. Instead use a spy on a real instance.

Here is a working example.

@Mock
private SomeService someService;

@InjectMocks
private SomeClass classUnderTest;

@Before
public void setUp() {
Mockito.when(someService.getData()).thenReturn(Collections.singletonList("Mock"));
}

@Test
public void methodToTest_someServiceCalled1() {

SomeClass spy = Mockito.spy(classUnderTest);
Mockito.when(spy.checkData(Mockito.any())).thenReturn(true);

List<String> result = spy.methodToTest(new SomeData());
Mockito.verify(someService).getData();
assertEquals("Mock", result.get(0));
}

I am not sure about you're 2nd test, afaik I would call the original checkData method. As you mentioned that is something you want to avoid, the solution should be similiar to the 1st test.

Inject Single Mocked Object together with Spring Components into Component Constructor

You are making things way too complex. Use @MockBean and Spring Boot will do the rest. Rewrite your test to the following

@SpringBootTest
class ATest {
@Autowired A service;
@MockBean B mockedService;

@Test
void testFoo() {
service.foo();
...
}
}

That is it, nothing more, nothing less. Spring will now use the mocked dependency and inject it into the service.

For more information on testing with Spring Boot I strongly suggest a read of the Testing section in the reference guide. It also has a whole section on mocking when using Spring Boot and testing.



Related Topics



Leave a reply



Submit