15

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?

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Victor Tsai
  • 167
  • 1
  • 1
  • 7
  • 2
    I think you need MockitoJunitRunner.class if you are trying to mock them. Also use when() stub to mock the function as you used for mUserInfoService. – Rohith K Jan 11 '17 at 07:03
  • Register the mock as a spring bean. You will now only receive the bean from the context and it will not mock anything (as you see). – M. Deinum Jan 11 '17 at 07:37

8 Answers8

16

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;

Oleg Ushakov
  • 1,200
  • 14
  • 27
14

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.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Andreas
  • 326
  • 2
  • 5
9

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.

alayor
  • 4,537
  • 6
  • 27
  • 47
  • 1
    thanks it work for me to manually inject the mock class. It seems that my @InjectMocks annotation really not working because I'v seen some article that work in same scenario http://stackoverflow.com/questions/34067956/how-to-use-injectmocks-along-with-autowired-annotation-in-junit – Victor Tsai Jan 13 '17 at 03:59
7

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.

Seba Cervantes
  • 199
  • 1
  • 7
  • 1
    This was it for me! Also can't use `static` either – David Liao Jun 28 '22 at 18:46
  • 1
    Yeah, you saved my time. The same case: i had repository as a final field in Service. Just trying to find out why mockito does not support injecting of final fields – 27P Dec 21 '22 at 17:41
6

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;
Derek Nash
  • 61
  • 1
  • 3
  • This fixed it for me. Thank you! It's funny that I have other tests that are using **@Mock and @InjectMocks** in a different unit test class that works, but it wouldn't work in this new test class of mine until I made this switch. – Mark Brown Jan 11 '22 at 20:04
3

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.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Raj Shukla
  • 442
  • 5
  • 9
1

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");

jAvA
  • 557
  • 1
  • 8
  • 22
1

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);
    }

    ...
}
Saša
  • 4,416
  • 1
  • 27
  • 41
Rajshree Rai
  • 533
  • 7
  • 14