简体   繁体   中英

@InjectMocks inject @MockBean by Constructor and setter not working properly

I have try so many time by unsing @RunWith(SpringJUnit4ClassRunner.class) I have tried to create a test case foe a class with getter and Constructor injection. When i user @MockBean for setter injection, @Mock for Constructor injection and also use @RunWith(SpringJUnit4ClassRunner.class) and MockitoAnnotations.initMocks(this); bean injection. If i comment MockitoAnnotations.initMocks(this); constructor injection not working. Now all beans are injected perfectly but @Mock beans(Contructor injected ) beans mocked mthods not working properly when its called.

@Component
Class A{
}

@Component
Class B {
}

@Component
Class c{
}

@Component
Class D{
@Atowired
A a;

B b;
C c;
@Autowired
public D(B b,C c){
b=b;
c=c;
}
}

My Test Class is

@RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
@MockBean
A mockA
@Mock
B mockB
@Mock
C mockC
@InjectMocks
D mockD

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);//Without this Constructor injection not working
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");

}
@Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in @before 
}
}

The injections are working properly,issue is belongs to mocked methods of beans which i use @Mock is not working properly means mockB.getValue() and mockC.getValue() retun null but mockA.getValue() return correctly when i test run.

If you are running a test with SpringJUnit4ClassRunner.class then you need to use @MockBean instead of @Mock .

Please refer to the spring boot documentation

Also, you need to use @Autowired instead of @InjectMocks .

@RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
@MockBean
A mockA
@MockBean
B mockB
@MockBean
C mockC
@Autowired
D mockD

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);//Without this Constructor injection not working
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");

}
@Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in @before 
}
}

When you run the test with spring runner, you must specify what exactly would you like to load as beans (read, let spring know what exactly should be included into the application context).

Usually this can be done with @ContextConfiguration annotation.

I suspect that since you don't specify this annotation, spring doesn't really loads any of your components (A, B, C in the question, etc).

Now @MockBean basically allows "altering" the application context for test purposes. It does so by providing a mock instead of a real bean that should have been loaded in "regular" application context.

In this case, there is no point to call MockitoAnnotations.initMocks(this); Spring will inject the mocks by itself once everything is configured properly.

Normal bean initialize methods like SpringJUnit4ClassRunner or MockitoAnnotations.initMocks(this); handling only one type injection at a time. Either constructor or Auto wired injection.

@RunWith(SpringJUnit4ClassRunner.class) means public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner. SpringJUnit4ClassRunner is a custom extension of JUnit's. It will initialize mock the @MockeBean and @bean anotted beans at the intial time of test run.Alsoi runnig the bean injection also.

MockitoAnnotations.initMocks(this) method has to called to initialize annotated fields. In above example, initMocks() is called in @Before (JUnit4) method of test's base class. For JUnit3 initMocks() can go to setup() method of a base class.

So that in above question you have used the SpringJUnit4ClassRunner and MockitoAnnotations.initMocks(this); it will create two mock bean references for which ever you use the @Mock . Also in above code flow.

1.At the beginning the SpringJUnit4ClassRunner run it will create bean reference for @Mock and @MockBean annotated attributes.after that it will create injected bean at tjis time only happens the constructor injection

2.Run @Before and Run MockitoAnnotations.initMocks(this); it will create another mock references for @Mock annotated attributes and replaced direct reference only. and Auto wired injection run at this time.

  1. After running the MockitoAnnotations.initMocks(this); you will see the allk beans are initialized and injected well. But beans which are injected thruogh constructor those beans are not correct reference these are referring old bean reference which are created by SpringJUnit4ClassRunner.

If you want to achieve the constructor and Auto wired injection for a single bean you must to use manual bean injected for the constructor injection. you can refer here

Solution:

@RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
@MockBean
A mockA
@MockBean
B mockB
@MockBean
C mockC
@Autowired
D mockD

@Before
public void setUp() {
mockD = new D(mockA,mockB);
MockitoAnnotations.initMocks(this);
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");

}
@Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in @before 
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM