I'm using Mockito to test my Spring project, but the @InjectMocks
seems not working in injecting a mocked service into another Spring service(bean).
Here is my Spring service that I want to test:
@Service
public class CreateMailboxService {
@Autowired UserInfoService mUserInfoService; // this should be mocked
@Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
}
And below is the service that I want to mock:
@Service
public class UserInfoService {
public String getData() {
return "original text";
}
}
My test code is here:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
@Mock
UserInfoService mUserInfoService;
@InjectMocks
@Autowired
CreateMailboxService mCreateMailboxService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void deleteWithPermission() {
when(mUserInfoService.getData()).thenReturn("mocked text");
System.out.println("test 1: " + mUserInfoService.getData());
mCreateMailboxService.createMailbox();
}
}
but the result would like
test 1: mocked text
test 2: original text // I want this be "mocked text", too
it seems that the CreateMailboxService didn't get the mocked UserInfoService but using Spring's autowired bean. Why is my @InjectMocks
not working?
For those who stumbles on this thread and are running with JUnit 5 you need to replace @RunWith(SpringJUnit4ClassRunner.class)
with
@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
Further reading here . Unfortunately there is no hint when executing the test cases with JUnit 5 using the old annotation.
In my case, I had a similar issue when I worked with JUnit5
@ExtendWith(MockitoExtension.class)
class MyServiceTest {
...
@InjectMocks
MyService underTest;
@Test
void myMethodTest() {
...
}
underTest
was null. The cause of the problem was that I used @Test
from JUnit4 package import org.junit.Test;
instead JUnit5 import org.junit.jupiter.api.Test;
You can create package
level setter for mUserInfoService
in CreateMailboxService
class.
@Service
public class CreateMailboxService {
@Autowired UserInfoService mUserInfoService; // this should be mocked
@Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
void setUserInfoService(UserInfoService mUserInfoService) {
this.mUserInfoService = mUserInfoService;
}
}
Then, you can inject that mock in the test using the setter.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
@Mock
UserInfoService mUserInfoService;
CreateMailboxService mCreateMailboxService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mCreateMailboxService = new CreateMailboxService();
mCreateMailboxService.setUserInfoService(mUserInfoService);
}
...
}
This way you can avoid problems with @InjectMocks
and Spring annotations.
If you are trying to use the @Mock annotation for a test that relies directly on Spring injection, you may need to replace @Mock with @MockBean @Inject (both annotations), and @InjectMocks with @Inject . Using your example:
@MockBean
@Inject
UserInfoService mUserInfoService;
@Inject
CreateMailboxService mCreateMailboxService;
I had a pretty similar situation. I am writing it down just in case any reader is going through the same. In my case I found that the problem was that I was setting my injected variable as final
in the local class.
Following your example, I had things like this:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
@Mock
UserInfoService mUserInfoService;
@InjectMocks
CreateMailboxService mCreateMailboxService = new CreateMailboxService(mUserInfoService);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void deleteWithPermission() {
...
}
}
But in this class I had it like this:
@Service
public class CreateMailboxService {
private final UserInfoService mUserInfoService; // it is NOT injecting Mocks just because it is final! (all ok with private)
private final LogicService mLogicService; // it is NOT injecting Mocks just because it is final! (all ok with private)
@Autowired
public CreateMailboxService(UserInfoService mUserInfoService, LogicService mLogicService) {
this.mUserInfoService = mUserInfoService;
this.mLogicService = mLogicService;
}
public void createMailbox() {
...
}
}
Just deleting the final
condition, @InjectMocks
problem was solved.
For those who are running with JUnit 5 you need to replace the @RunWith(SpringJUnit4ClassRunner.class)
with @ExtendWith(MockitoExtension.class)
.
For further reading take a look here .
there is no need of @Autowired annotation when you inject in the test class. And use the mock for the method to get your mocked response as the way you did for UserInfoService.That will be something like below. Mockito.when(mCreateMailboxService. getData()).thenReturn("my response");
You can use MockitoJUnitRunner to mock in unit tests. Use @Mock annotations over classes whose behavior you want to mock. Use @InjectMocks over the class you are testing. Its a bad practice to use new and initialize classes (better to go for dependency injection) or to introduce setters for your injections. Using setter injection to set dependencies only for tests is wrong as production code should never be altered for tests.
@RunWith(MockitoJUnitRunner.class)
public class CreateMailboxServiceMockTest {
@Mock
UserInfoService mUserInfoService;
@InjectMocks
CreateMailboxService mCreateMailboxService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
...
}
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.