Using #Mock and #InjectMocks - java

I'm currently studying the Mockito framework and I've created several test cases using Mockito.
But then I read that instead of invoking mock(SomeClass.class) I can use the #Mock and the #InjectMocks - The only thing I need to do is to annotate my test class with #RunWith(MockitoJUnitRunner.class) or use the MockitoAnnotations.initMocks(this); in the #Before method.
But it doesn't work - It seems that the #Mock won't work!
Here is my 2 codes revisions - one using the annotations and one without.
What am I doing wrong?
public class ReportServiceImplTestMockito {
private TaskService mockTaskService; // This is the Mock object
private ReportServiceImpl service;
#Before
public void init(){
service = new ReportServiceImpl();
mockTaskService = mock(TaskServiceImpl.class);
service.setTaskServiceImpl(mockTaskService);
}
/// ...
Some tests
}
As I said - this work great.
But the following wont:
#RunWith(MockitoJUnitRunner.class)
public class ReportServiceImplTestMockito {
#Mock
private TaskService mockTaskService;
#InjectMocks
private ReportServiceImpl service;
// Some tests
}
And here is the ReportServiceImpl class:
#Service
public class ReportServiceImpl implements ReportService {
#Autowired
private TaskService taskServiceImpl;
public ReportServiceImpl(){}
public ReportServiceImpl(TaskService taskService){
this.taskServiceImpl = taskService;
}
public void setTaskServiceImpl(TaskService taskServiceImpl) {
this.taskServiceImpl = taskServiceImpl;
}
}
What am I missing?

O.K, I got my mistake!!!
I've used the #InjectMocks but initialized the same variable in the init() method...
So what happened was that mockito injected the mock objects to my variable - but seconds later I ran it over - initializing that very same variable!!!

Your code works fine for me using Mockito 1.9.
Using an 1.8+ version of Mockito I get a very specific error message telling me exactly how to fix the problem. As php-coder suggests: For Mockito 1.8+ you need to initialize the field.
Did you see this or any other error message?
Edit:
The following code works for me. Small changes:
Removed Spring annotations
Removed Interface
Added Getter
Added empty TaskService
Added test with System.out.println
Does it produce an error for you? :
Service:
public class ReportServiceImpl {
private TaskService taskServiceImpl;
public ReportServiceImpl() {
}
public ReportServiceImpl(TaskService taskService) {
this.taskServiceImpl = taskService;
}
public void setTaskServiceImpl(TaskService taskServiceImpl) {
this.taskServiceImpl = taskServiceImpl;
}
public TaskService getTaskServiceImpl() {
return taskServiceImpl;
}
}
Dependency:
public class TaskService {
}
Test, prints mockTaskService:
#RunWith(MockitoJUnitRunner.class)
public class ReportServiceImplTestMockito {
#Mock
private TaskService mockTaskService;
#InjectMocks
private ReportServiceImpl service;
#Test
public void testMockInjected() {
System.out.println(service.getTaskServiceImpl());
}
}

I'm not sure, but try to create new instance of ReportServiceImpl manually (as you did in working example):
#InjectMocks
private ReportServiceImpl service = new ReportServiceImpl();

Related

How to Test void Method with spring repository using Junit and Mockito

i have a java spring service that call a spring data repository and i want to do a Junit test with mockito , this is my class and the service that i want to test :
#Service
public class DataServiceImpl implements DataService{
#Autowired
private CautionRepository cautionRepository;
#Override
public void addCautions(List<CautionsDTO> cautions, Contrat contrat) {
if(cautions != null && !cautions.isEmpty()) {
cautions.forEach(caution -> {
Caution caution = new Caution();
cautionContrat.setAmount(caution.getAmount());
cautionContrat.setDate(caution.getDate());
caution.setContrat(contrat);
cautionRepository.save(caution);
});
}
}
}
and this is my Unit test
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#InjectMocks
private DataServiceImpl dataService;
#Mock
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
Caution caution = new Caution();
dataDelService.addCautions(cautions,contrat);
Mockito.verify(cautionRepository, times(1)).save(caution);
}
}
When i run the test i got the folowwing error :
Wanted but not invoked:
cautionRepository.save(
org.model.Caution#2abe9173
);
-> at org.service.DataServiceImplTest.addListCautionsTest(DataServiceImplTest.java:292)
Actually, there were zero interactions with this mock.
Do you have any idea please what is the mistake with my test
You never add a value to cautions so the loop is not executed and verify must fail.
Add a value to the list and the test should pass:
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
CautionDTO caution = new CautionDTO();
cautions.add(caution);
dataDelService.addCautions(cautions,contrat);
This should be the correct code for executing the test
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#Autowired
private DataServiceImpl dataService;
#MockBean
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
Caution caution = new Caution();
dataDelService.addCautions(cautions,contrat);
Mockito.verify(cautionRepository, times(1)).save(caution);
}
}
First of all, you forgot to add caution object into cautions list. But other than that you are mixing Unit test with Integration test.
You need to annotate your test class with #RunWith(MockitoJUnitRunner.class),
or
You need to annotate your mock objects with #MockBean and add #Autowired to your test class DataServiceImpl object.
Now, let me explain to you.
Unit Test
When you want to write a unit test, you should not make use of application context (autowiring).
By the way, a better approach is to annotate your DataServiceImpl with #RequiredArgsConstructor from Lombok and remove #Autowired from CautionRepository. This will allow you to instantiate DataServiceImpl in a setup method in your unit test.
Your DataServiceImpl class should be:
#Service
#RequiredArgsConstructor
public class DataServiceImpl implements DataService{
private final CautionRepository cautionRepository;
#Override
public void addCautions(List<CautionsDTO> cautions, Contrat contrat) {
// your code...
}
}
and your new unit test class:
#RunWith(MockitoJUnitRunner.class)
public class DataServiceImplTest{
private DataServiceImpl dataService;
#Mock
private CautionRepository cautionRepository;
#Before
public void setup() {
dataService = new DataServiceImpl(cautionsRepository);
}
#Test
public void addListCautionsTest() {
// your test code...
}
}
Integration Test
Now, if you want to create an integration test, use #RunWith(SpringRunner.class). By doing this your application context will be loaded. In your case you can create a mocked bean inside your context by annotating your object with #MockBean. This will inject mocked object into your context and it will get auto wired in your real class.
For this your new DataServiceImpl class can remain same as above. But change your integration test into:
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#Autowired
private DataServiceImpl dataService;
#MockBean // it will be injected automatically
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
// your test code...
}
}
Hope, you now understand the difference and the mistake you were doing :)

Mockito when isn't replacing original method behaviour

I got 2 modules User and Email, both of them have 1 entry point which is a facade, rest is package scoped. The configuration is done in 2 classes
#Configuration
class UserConfiguration {
#Bean
UserFacade userFacade(UserRepository repository, EmailFacade emailFacade) {
return new UserFacade(repository, emailFacade);
}
}
#Configuration
class EmailConfiguration {
#Bean
EmailFacade emailFacade(EmailSender emailSender) {
return new EmailFacade(emailSender);
}
}
Now, I want to write tests that don't require Spring to start. I implemented a simple InMemoryRepository to make this happen
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade = new EmailFacade(new FakeEmailSender());
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I need some fake objects to instantiate EmailFacade so I wrote fake implementation
public class FakeEmailSender implements EmailSender {
#Override
public void sendEmail(EmailMessage emailMessage) throws RuntimeException {
}
}
In that scenario, I'm testing User domain, so I want to mock Email anyways.
I wrote a test to check if it works
#Test
public void shouldReturnSendingFailed() {
Mockito.when(emailFacade.sendUserVerificationEmail(Mockito.any())).thenReturn(Either.left(EmailError.SENDING_FAILED));
assertThat(userFacade.registerNewUser(RegisterUserDto.builder()
.username(USERNAME_4)
.email(VALID_EMAIL)
.password(VALID_PASSWORD).build()).getLeft(), is(EmailError.SENDING_FAILED));
}
But it isn't... after running this test I got
java.util.NoSuchElementException: getLeft() on Right
edit#
regiserNewUser() method
Either<DomainError, SuccessMessage> register(RegisterUserDto registerUserDto) {
if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
return Either.left(UserError.USERNAME_ALREADY_EXISTS);
var userCreationResult = User.createUser(registerUserDto);
var savedUser = userCreationResult.map(this::saveUser);
var emailDto = savedUser.map(this::createVerificationEmail);
return emailDto.isRight() ? emailFacade.sendUserVerificationEmail(emailDto.get())
: Either.left(emailDto.getLeft());
}
Edit2#
With following test configuration
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I got nullpointer here, last line of registerNewUser().
Try running this code
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
private UserFacade userFacade;
#Before
public void setUp() {
userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
}
}
There are a few issues with your code:
You initialize your mocks twice. You don’t need to call initMocks in the setUp method if you are using Mockito runner
You are trying to inject mocks to already initialized object. But the field you are trying to inject is also passed to the constructor. Please read #InjectMocks doc, to check the strategies used to inject the mocks:
constructor (not used here, already initialized object)
setter (do you have one?)
field (is it not final)
There are details to each strategy (see my questions above). If no staregy is matched, Mockito will fail silently. The fact that you are passing an object in constructor, and rely on setter or field injection afterwards makes this code unnecesarily complex.

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

Using #InjectMocks to replace #Autowired field with a mocked implementation

I want to replace an autowired class of a service in my spring boot app with a mocked implementation of that class that I created specifically for testing.
I chose to create this mocked implementation because the behaviour of this class is too complicated to mock using mockito as it requires multiple other mocks itself.
I am not able to work out how to inject this mocked implementation into the service.
Here is a minimal example of the situation:
#Service
public class ServiceIWantToTestImpl implements ServiceIWantToTest{
#Autowired
ComplicatedDependency complicatedDependency;
#Override
public void methodUsingDependency(){
String string = complicatedDependency.doSomething();
System.out.println(string);
}
}
public class MockComplicatedDependency implements ComplicatedDepencency{
public MockComplicatedDependency(...){
// Inject other mocked objects into this mock
}
public String doSomthing(){
// This would be a mocked version of this function for testing
return "test";
}
}
#RunWith(MockitoJUnitRunner.class)
public class TestingTheService(){
#InjectMock
private static ServiceIWantToTest serviceIWantToTest = new ServiceIWantToTestImpl();
#Mock
ComplicatedDependency mockComplicatedDependency;
#BeforeClass
public static void init(){
mockComplicatedDependency = new MockComplicatedDependency(...);
}
#Test
public void testAttempt(){
serviceIWantToTest.methodUsingDependency(); // This method calls complicatedDependency.doSomething() which does not run the mocked version in MockComplicatedDependency which I wanted to inject, and would always return null instead of the "test" string I put in this example.
}
}
Do you have to use Mockito annotations to setup dependencies for the class under test?
If that is not the main constraint why not just do the plain simple setup and introduce a constructor or a setter in ServiceIWantToTestImpl class for the ComplicatedDependency field and set the dependency in your test setup directly to whatever impl of ComplicatedDependency you like e.g.:
#Service
public class ServiceIWantToTestImpl implements ServiceIWantToTest {
#Autowired
ComplicatedDependency complicatedDependency;
public ServiceIWantToTestImpl() {
}
public ServiceIWantToTestImpl(ComplicatedDependency complicatedDependency) {
this.complicatedDependency = complicatedDependency;
}
#Override
public void methodUsingDependency(){
String string = complicatedDependency.doSomething();
System.out.println(string);
}
}
public class TestingTheService {
private static ServiceIWantToTestImpl serviceIWantToTest;
#BeforeClass
public static void init(){
serviceIWantToTest = new ServiceIWantToTestImpl(new MockComplicatedDependency());
}
#Test
public void testAttempt() {
serviceIWantToTest.methodUsingDependency();
}
}
That is one way.
To make it work with Mockito, You could to use #Spy instead of #Mock like this:
#RunWith(MockitoJUnitRunner.class)
public class TestingTheService {
#InjectMocks
private static ServiceIWantToTestImpl serviceIWantToTest = new ServiceIWantToTestImpl();
#Spy
private static ComplicatedDependency complicatedDependency = new MockComplicatedDependency();
#BeforeClass
public static void init() {
}
#Test
public void testAttempt() {
serviceIWantToTest.methodUsingDependency();
}
}
Though this is a bit of a hack. I strongly recommend that you read the JavaDoc of the #Spy annotation and make sure it's expected use is what you really need for your test.

Spy a method from parent class in a class with injected mocks with Mockito

I'm using Mockito in a Java project with Spring and Struts and I'm having problems with testing actions.
I'm not using the Struts2 jUnit plugin to save time with the tests using this approach: Strut 2.3.1.2 Unit test, how to remove Spring codependency vs NPE with getContext().
The problem is when in my action, when getText() is called I've got a NullPointerException.
I'm trying to spy this method, that's inherited from ActionSupport but I don't find a way because the action is annotated with InjectMocks in the test.
Here's a simplied example of the classes:
Parent:
public class ActionSupport {
public String getText(String aTextName){
return this.getTextProvider().getText(aTextName);
}
}
My action:
public class MyAction extends ActionSupport {
#Autowired private IMyService myService;
public String execute(){
getText("SomeText");
myService.something();
return SUCCESS;
}
}
Test:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock private IMyService myService;
#InjectMocks private MyAction myAction;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myAction.execute();
}
}
I'm getting this exception:
java.lang.NullPointerException
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:167)
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:126)
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:48)
at com.opensymphony.xwork2.util.LocalizedTextUtil.getDefaultMessage(LocalizedTextUtil.java:663)
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:534)
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:362)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:208)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:123)
at com.opensymphony.xwork2.ActionSupport.getText(ActionSupport.java:103)
at es.MyAction.execute(MyAction.java:148)
And if I annotate MyAction with #Spy maintaining #InjectMocks I've got an StackOverflowError.
How can I spy only ActionSupport.getText() and let mockito inject mocks on my action?
I would avoid using #InjectMocks as it fails silently.
Just add a constructor to your MyAction still using #Autowired i.e. constructor injection. This also helps guarantee required dependencies.
You dont need both initMocks and MockitoJUnitRunner.
public class MyAction extends ActionSupport {
private IMyService myService;
#Autowired
public MyAction(MyService myService) {
this.myService = myService;
}
public String execute(){
getText("SomeText");
myService.something();
return SUCCESS;
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock
private IMyService myService;
private MyAction myAction;
#Before
public void setup() {
myAction = spy(new MyAction(myService));
}
#Test
public void test() {
assertThat(myAction.execute(), equalTo(Action.SUCCESS));
verify(myAction, times(1)).getText();
verify(myService, times(1)).something();
}
}
InjectMocks fails silently
Constructor injection discussion

Categories

Resources