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.
Related
I'm using Mockito 3.4.6 in unit test, actually, i have integrated Mockito to my unit test and it works well. While, now i need to optimize some unit test, it's a special dependency injection that the injected object doesn't have no-arg constructor, I tried #Spy but it didn't work.
My Test: I tried 1. #Spy; 2. #Spy with setting instance using = getDtInsightApi(); 3. #Spy with #InjectMocks, all of tests are failed. As Mockito docs said, seems it can't work for this case.
#InjectMocks Mockito will try to inject mocks only either by constructor injection,
setter injection, or property injection in order and as described below.
Also if only use #Spy, it will throw MockitoException:
org.mockito.exceptions.base.MockitoException:
Failed to release mocks
This should not happen unless you are using a third-part mock maker
...
Caused by: org.mockito.exceptions.base.MockitoException: Unable to initialize #Spy annotated field 'api'.
Please ensure that the type 'DtInsightApi' has a no-arg constructor.
...
Caused by: org.mockito.exceptions.base.MockitoException: Please ensure that the type 'DtInsightApi' has a no-arg constructor.
See my pseudocode as below:
configure class:
#Configuration
public class SdkConfig {
#Resource
private EnvironmentContext environmentContext;
#Bean(name = "api")
public DtInsightApi getApi() {
DtInsightApi.ApiBuilder builder = new DtInsightApi.ApiBuilder()
.setServerUrls("sdkUrls")
return builder.buildApi();
}
}
DtInsightApi class with no public no-arg constructor and get instance by its inner class
public class DtInsightApi {
private String[] serverUrls;
DtInsightApi(String[] serverUrls) {
this.serverUrls = serverUrls;
}
// inner class
public static class ApiBuilder {
String[] serverUrls;
public ApiBuilder() {
}
...code...
public DtInsightApi buildApi() {
return new DtInsightApi(this.serverUrls);
}
}
...code...
}
unit test class:
public Test{
#Autowired
private PendingTestService service;
#Spy
private Api api = getDtInsightApi();
#Mock
private MockService mockService;
#Before
public void setUp() throws Exception {
// open mock
MockitoAnnotations.openMocks(this);
// i use doReturn(...).when() for #Spy object
Mockito.doReturn(mockService).when(api)
.getSlbApiClient(MockService.class);
Mockito.when(mockService.addOrUpdate(any(MockDTO.class)))
.thenReturn(BaseObject.getApiResponseWithSuccess());
}
public DtInsightApi getDtInsightApi () {
return new DtInsightApi.ApiBuilder()
.setServerUrls(new String[]{"localhost:8080"})
.buildApi();
}
#Test
public void testUpdate() {
service.update();
}
}
PendingTestService:
#Service
public class PendingTestService{
#Autowired
DtInsightApi api;
public void update() {
// here mockService isn't the object i mocked
MockService mockService = api.getSlbApiClient(MockService.class);
mockService.update();
}
}
Question: How to mock the DI object DtInsightApi which doesn't have no-arg constructor.
After checked Spring docs about unit test, I found a solution using #MockBean.
Spirng docs:https://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/html/boot-features-testing.html
According to Spring docs, you can use #MockBean to mock a bean inside your ApplicationContext, so i can use #MockBean to mock DtInsightApi.
It’s sometimes necessary to mock certain components within your application context when running tests. For example,
you may have a facade over some remote service that’s unavailable during development. Mocking can also be useful when
you want to simulate failures that might be hard to trigger in a real environment.
Spring Boot includes a #MockBean annotation that can be used to define a Mockito mock for a bean inside your ApplicationContext.
You can use the annotation to add new beans, or replace a single existing bean definition. The annotation can be used directly on test classes,
on fields within your test, or on #Configuration classes and fields. When used on a field, the instance of the created mock will also be injected.
Mock beans are automatically reset after each test method.
My Solution: Use #MockBean and BDDMockito.given(...).willReturn(...), use
#Qualifier("api") to specify the bean name because #MockBean injected by class type, if you have same class beans, you need to specify bean name.
My code in test class:
public class Test{
#MockBean
#Qualifier("api")
private DtInsightApi api;
#Mock
private MockService mockService;
#Before
public void setUp() throws Exception {
// open mock
MockitoAnnotations.openMocks(this);
BDDMockito.given(this.api.getSlbApiClient(MockService.class)).willReturn(mockService);
}
#Autowired
private PendingTestService service;
#Test
public void testUpdate() {
service.update();
}
}
Debug the mockService you can see mockService instance is generated by Mockito, mock succeed.
You can also refer to Spring docs example: mock RemoteService in unit test.
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;
import org.springframework.test.context.junit4.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTests {
#MockBean
private RemoteService remoteService;
#Autowired
private Reverser reverser;
#Test
public void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}
}
I have a Service A which autowired Service B and is using a method from Service B.
Service B autowired another Service C and is using a method from it.
I am writing a test for Service A and the test fails at the call where Service B is invoked.At this point Service C is null.
I have tried #Mock for Service B. Nothing seems to work. How can i successfully test this service which is failing on a service that it isnt explicitly autowiring.
//Service A
#Service
public class FileServiceImpl{
#Autowired
private FileNameServiceImpl fileNameService;
public void createFile(String fileName){
String targetFileName = fileNameService.getTargetFileName(fileName);
}
}
//Service B
#Service
public class FileNameServiceImpl{
#Autowired
private CustomDateService customDateService
public String getTargetFileName(String fileName){
return fileName + customDateService.getCustomDate();
}
}
//CustomDate - this is an interace. The Impl is in another package.
public interfaceCustomDateService{
public String getCustomDate();
}
I am trying to test FileServiceImpl , however it fails with a NullPointer Exception because customDateService is null.
Even though, FileServiceImpl is not calling customDateService.
This is what I have for test thus far:
#Category(UnitTest.class)
#RunWith(MockitoJUnitRunner.class)
public class FileServiceImplTest {
#Spy
#InjectMocks
private FileServiceImpl fileServiceImpl;
#Mock
private FileNameServiceImpl fileNameService;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testFileName() {
String fileName = "test1.txt";
fileServiceImpl.createFile(fileName); // Test Fails here
Mockito.validateMockitoUsage();
}
As Shane eluded, this sounds like you're maybe integration testing.
If so, make sure the context of your test encompasses the autowired components.
You should post some code, as it's hard to know what exactly is going on here.
If you aren't integration testing, don't rely on autowiring, just construct new ServiceA manually passing in a mocked ServiceB.
Also make sure to initialise your mocks.
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks();
serviceA = new ServiceA(serviceB);
}
EDIT:
First of all, as good practice you should favor constructor injection over field injection in Spring.
So set up the service classes with Autowired constructors.
Also, I'm pretty sure with a mockito Spy you have to initialise the class.
If you switch to use constructor Autowiring you can inject the mocks manually.
#Category(UnitTest.class)
#RunWith(MockitoJUnitRunner.class)
public class FileServiceImplTest {
#Spy
private FileServiceImpl fileServiceImpl;
#Mock
private FileNameServiceImpl fileNameService;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
fileServiceImpl = new FileServiceImpl(fileNameService);
}
#Test
public void testFileName() {
String fileName = "test1.txt";
fileServiceImpl.createFile(fileName); <-- now this shouldn't fail
Mockito.validateMockitoUsage();
}
I have a Spring-boot project, in witch I have controller, service and mapper layer. Now I want to test a service and I want to mock the mapper. I do it in this way:
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class)
#Transactional
public class SomeServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
#Mock
private AMapper aMapper;
#Autowired
#InjectMocks
AService aService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
executeSqlScript("classpath:insertSomeData.sql", false);
}
#Test
public void testMethod() throws Exception {
//prepareSomeData
aService.callMethod(someData);
verify(aMapper).callTheRightMethod(rightObject);
}
And the service:
#Service
#Transactional(readOnly = true)
public class AServiceImpl implements AService {
#Autowired
BMapper bMapper;
#Autowired
CMapper cMapper;
#Override
#Transactional(readOnly = false)
public SomeReturnObject callMethod(SomeData someData)throws Exception {
//some execution to obtain aResult
if(true){
aMapper.callTheRightMethod(aResult);}
else
aMapper.callWrongMethod(aResult);
}
Now when I execute the test the result is:
Wanted but not invoked:
aMapper.callTheRightMethod{..}
Actually, there were zero interactions with this mock.
When i debug then I see that the method is called, but probably it's the wrong mapper (not the mocked). Have you some tips to figure out that issue?
I can't see the mock interaction recording here. It should come before the actual invocation. It should be something like this.
Mockito.when(aMapper.callTheRightMethod(Mockito.any()).thenReturn(rightObject);
The flow should be like this. Firstly record the mocks, then perform actual invocation and finally verify the mock interactions. As above #Autowire is not needed for the test class. Please remove that too. Instead create a new instance of service class by passing some data through it's constructor. Hope this helps. Happy coding !
I don't exactly understand why would you start up spring context for testing just a service layer. Test only one layer at a time.
That's how I would address the problem. (If something does not compile, my apologies..writing from top of my head)
#RunWith(MockitoJUnit4ClassRunner.class)
public class SomeServiceTest {
#Mock
private AMapper aMapper;
#InjectMocks
AService aService = new AService();
#Test
public void testMethod() throws Exception {
// given
Mockito.doReturn(aResult).when(aMapper).getAResult();
// when
aService.callMethod(someData);
// then
verify(aMapper).callTheRightMethod(rightObject);
}
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);
}
...
}
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,