简体   繁体   中英

Injecting mocks with Mockito does not work

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.

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