Mockito inject nested bean - java

Im not new to mockito, but this time I found an interesting case during my work. I hope you can help me out with it.
I need to inject mock to change certain method behaviour during the test. The problem is, the bean structure is nested, and this bean is inside other beans, not accessible from test method. My code looks like this:
#Component
class TestedService {
#Autowired
NestedService nestedService;
}
#Component
class NestedService {
#Autowired
MoreNestedService moreNestedService;
}
#Component
class MoreNestedService {
#Autowired
NestedDao nestedDao;
}
#Component
class NestedDao {
public int method(){
//typical dao method, details omitted
};
}
So in my test I would like the call NestedDao.method to return mocked answer.
class Test {
#Mock
NestedDao nestedDao;
#InjectMocks
TestedService testedSevice;
#Test
void test() {
Mockito.when(nestedDao.method()).thenReturn(1);
//data preparation omitted
testedSevice.callNestedServiceThatCallsNestedDaoMethod();
//assertions omitted
}
}
I have tried to do a initMocks:
#BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Also to add annotation upon my test class:
#RunWith(MockitoJUnitRunner.class)
Always getting nullpointers or wrong answer from method (not mocked).
I guess it's this nested calls fault, making it impossible to mock this Dao.
Also Ive read that #InjectMocks only would work with setters or constructor injection, which im missing (just #Autowire on private fields), but it didn't worked when I tried.
Any guess what am I missing? ;)

You could use #MockBean instead of #Mock and #InjectionMock.
#RunWith(SpringRunner.class)
#SpringBootTest
class Test {
#MockBean
NestedDao nestedDao;
#Autowired
TestedService testedSevice;
#Test
void test() {
Mockito.when(nestedDao.method()).thenReturn(1);
//data preparation omitted
testedSevice.callNestedServiceThatCallsNestedDaoMethod();
//assertions omitted
}
}

It makes sense to me, you are so wrong,
why?
It's because you're testing TestedService and the interaction with NestedService, not with the Dao, the Dao interaction should be verified on the NestedService tests
Look this:
#Component
class TestedService {
#Autowired
NestedService nestedService;
String sayHello(String name){
String result = "hello" + nestedService.toUpperCase(name)
}
}
#Component
class NestedService {
#Autowired
MoreNestedService moreNestedService;
String toUpperCase(String name){
String nameWithDotAtTheEnd = moreNestedService.atDot(name);
return nameWithDotAtTheEnd.toUpperCase();
}
}
On your test:
class Test {
#Mock
NestedService nestedService;
#InjectMocks
TestedService testedSevice;
#Test
void test() {
Mockito.when(nestedService.toUpperCase("rene")).thenReturn("RENE.");
//data preparation omitted
Assert.assertEquals("hello RENE.", testedSevice.sayHello("rene"));
//assertions omitted
}
}
As you can see, you are assuming the dependencies of TestedService are working well, you only need to verify that hello is being added as prefix of the string,

Related

Test void methods with Autowired classes

I am a beginner in Spring and I need to write test for this class if it calls methods:
class ClassOne {
#Autowired
AutowiredClass a1;
#Autowired
AutowiredClass a2;
void methodOne() {
a1.method1();
}
void methodTwo() {
a2.method2();
}
}
I've tried to write test, but failed, got NPE:
class ClassOneTest {
#Autowired
ClassOneInterface c1i;
#Test
public void testMethod1() {
c1i.methodOne(); // <- NPE appears here..
Mockito.verify(ClassOne.class, Mockito.times(1));
}
}
Would be great to successfully test void methods.
You can verify that using a unit test:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest {
#InjectMocks
private ClassOne c1 = new ClassOne();
#Mock
private AutowiredClass a1;
#Mock
private AutowiredClass a2;
#Test
public void methodOne() {
c1.methodOne(); // call the not mocked method
Mockito.verify(a1).method1(); //verify if the a1.method() is called inside the methodOne
}
}
ClassOne defines a spring bean. In order to Autowire the bean's fields you need a Spring Context.
If you want to test ClassOne as a SPRING BEAN, then you need to use SpringTest.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({MyConfig.class}) // Spring context config class
class ClassOneTest {
#Autowired
ClassOneInterface c1i;
...
}
A ClassOne bean will be injected into the test suite c1i field.
Then you can spy on the field using mockito:
ClassOne cSpy = spy(c1i);
Then you can verify method calls on it :
verify(cSpy).someMethod(someParam);
Hope this helps
You are getting a NPE because the loaded context on your test case cannot find the autowired bean you mentioned. You should annotate the test class like so:
#RunWith(SpringRunner.class)
#SpringBootTest
class ClassOneTest {
#Autowired
ClassOneInterface c1i;
#Test
public void testMethod1() {
c1i.methodOne(); // <- NPE appears here..
Mockito.verify(ClassOne.class, Mockito.times(1));
}
}
Consider taking a look at: Similar NPE test error

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

How to AutoWire spring beans when using Mockito and Junit?

I am trying to set up my class to be used in Junit.
However when I try to do the below I get an error.
Current Test Class:
public class PersonServiceTest {
#Autowired
#InjectMocks
PersonService personService;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
assertThat(PersonService, notNullValue());
}
//tests
Error:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'personService'
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
How can I fix this?
You are not mocking anything in your code. #InjectMocks sets a class where a mock will be injected.
Your code should look like this
public class PersonServiceTest {
#InjectMocks
PersonService personService;
#Mock
MockedClass myMock;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Mockito.doReturn("Whatever you want returned").when(myMock).mockMethod;
}
#Test()
public void testPerson() {
assertThat(personService.method, "what you expect");
}
Another solution is to use #ContextConfiguration annotation with static inner configuration class like so:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class PersonServiceTest {
#Autowired
PersonService personService;
#Before
public void setUp() throws Exception {
when(personService.mockedMethod()).thenReturn("something to return");
}
#Test
public void testPerson() {
assertThat(personService.method(), "what you expect");
}
#Configuration
static class ContextConfiguration {
#Bean
public PersonService personService() {
return mock(PersonService.class);
}
}
}
Anyway, you need to mock something that the method you want to test uses inside to get desired behaviour of that method. It doesn't make sense to mock the service you're testing.
You're misunderstanding the purpose of the mock here.
When you mock out a class like this, you are pretending as if it's been injected into your application. That means you don't want to inject it!
The solution to this: set up whatever bean you were intending to inject as #Mock, and inject them into your test class via #InjectMocks.
It's unclear where the bean you want to inject is since all you have is the service defined, but...
#RunWith(MockitoJUnitRunner.class);
public class PersonServiceTest {
#Mock
private ExternalService externalSvc;
#InjectMocks
PersonService testObj;
}
If I am not mistaken...the thumb rule is you cannot use both together..you either run unit test cases with using MockitojunitRunner or SpringJUnitRunner you cannot use both of them together.

Mockito to test an autowired field

public interface Dummy {
public returnSomething doDummyWork(arg1, agr2);
}
public class A implements Dummy {
#AutoWired
PrintTaskExecutor printTaskExecutor;
public returnSomething doDummyWork(arg1, agr2) {
callingVoidMethod();
return something;
}
public void callingVoidMethod() {
printTaskExecutor.printSomething(arg1, arg2);
}
}
public class testDummy {
#Autowired
Dummy dummyA//this bean is configured in ApplicationContext.xml and it works fine.
#Mock
PrintTaskExecutor printaskExecutor;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
printaskExecutor = Mockito.mock(PrintTaskExecutor.class);
Mockito.doNothing().when(printaskExecutor).printSomething(anyString(), anyString());
}
#Test
Public void testA
{
Dummy.doDummyWork(arg1, arg2);//I m giving actual arguments
//instead of moocking it calls the original method.
Mockito.verify(printaskExecutor, times(1)).printSomething(anyString(), anyString());
}
}
I have an autowired TaskExecutor in the class I m testing and I want to mock it.I have tried this in my code and It calls the actual method instead of do nothing and in the verify it errors out saying no interactions happened. How should I handle this situation?
I try to avoid using Mockito and Bean Containers together in one test. There are solutions for that problem. If you use Spring you should use #RunWith(SpringJUnit4Runner.class). More on this subject: Injecting Mockito mocks into a Spring bean
The clean way: Actually your class testDummy does not test Dummy but A. So you can rewrite your class in following way:
public class testA {
#Mock
PrintTaskExecutor printTaskExecutor;
#InjectMocks
A dummyA;
...
BTW: #Mock together with initMocks(this) and printaskExecutor = Mockito.mock(PrintTaskExecutor.class); do the same, you can skip the latter statement.

Mocked method is never getting called

I'm trying to write a test case for a java class. I've mocked a method using Mockito, but the mock is never being used. Why?
Below is the structure of my java class:
class A {
#Autowired
private ClassB classB;
publiv void methodOne() {
methodTwo();
}
private void methodTwo() {
...
int returnedValue = classB.someMethod();
...
}
}
The test class is given below:
class ATest {
#Mock
private ClassB classB;
#InjectMocks
#Autowired
ClassA classA;
#Before
public void setupTest() {
MockitoAnnotations.initMocks(this);
}
#Test(expected = SomeException.class)
public void testMethodOne() {
when(classB.someMethod()).thenReturn(29);
classA.methodOne();
}
}
The test class is extended from another which has the #RunWith(SpringJUnit4ClassRunner.class) annotation.
I've gone through the existing questions but have been unable to find an answer. If there is any question/answer that may help me, please point me to the same.
Thanks in advance!
You could try to remove #Autowired in your test class ATest.
#InjectMocks should be sufficient as it creates your test object and injects your mock.
Depending on your JUnit runner #Autowired might perform a second injection after #InjectMocks which overrides your mocked method setup.
First, a few notes on your test:
- the test should be public
- class A should be public
- there is no need for #Autowire in the test
- there is no need to call initMocks
Then, your test isn't actually testing if the mock is being used or not. Let's change these method so that they now return the value, so that we can test if they work or not:
public class ClassA {
#Autowired
private ClassB classB;
public int methodOne() {
return methodTwo();
}
private int methodTwo() {
return classB.someMethod();
}
}
public class ClassB {
public int someMethod() {
return 0;
}
}
We can now test:
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Mock
private ClassB classB;
#InjectMocks
ClassA classA;
#Test
public void testMethodOne() {
when(classB.someMethod()).thenReturn(29);
final int result = classA.methodOne();
assertEquals(29, result);
}
}
And the test passes - meaning the mock is being used correctly.
Hope that helps.
What you're missing is that annotations are just declarations. The #Mock and #InjectMocks annotations don't do anything. They are only markers that can be seen by the test runner to perform specific runner-specific behavior. You need a #RunWith annotation to specify a test runner, but you need a specific test runner for specific behavior. If you're using the SpringJUnit4ClassRunner, then none of the Mockito annotations will be used. In any case, I wouldn't want to use the SpringJUnit4ClassRunner for unit tests, as you should just be verifying business logic, not wiring.
Change your test class to use #RunWith(MockitoJUnitRunner.class) and remove the base class reference.

Categories

Resources