#MockBean and #Autowired of the same service in one test class - java

Is it possible to somehow have in the same test class #MockBean and #Autowired of the same service?
In other words, I would like to have #MockBean service only for one test, while for others tests of the same class I need it as #Autowired.

This relies on the difference between #MockBean and #Autowired.
#Autowired
only does a lookup in the SpringContext for a bean of that type. This means that you will need to create that bean if you need to 'autowire' it
#MockBean
does exactly what you expect from the name, it creates a 'mock' of the service, and injects it as a bean.
so this
class MyTest {
#MockBean
MyService myService;
}
is equivalent to this
#Import(MyTest.Config.class)
class MyTest {
#Autowired
MyService myService;
#TestConfiguration
static class Config {
#Bean
MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
So, if you need to have a different bean of the MyService type in other tests, you need to create the bean in a #TestConfiguration annotated class
#Import(MyTest.Config.class)
class MyTest {
#Autowired
MyService myService;
#TestConfiguration
static class Config {
#Bean
MyService myService() {
return new MyServiceImpl();
}
}
}
Or, in a class annotated with #Configuration
#Import(MyConfig.class)
class MyTest {
#Autowired
MyService myService;
}
#Configuration
public class MyConfig {
#Bean
MyService myService() {
return new MyServiceImpl();
}
}

The best solution is to change #MockBean to #SpyBean. And in the method you will be able to do like this:
kotlin
#SpyBean
lateinit var serviceMock: Service
#Test
fun smallTest()
`when`(serviceMock.doSomething())
.thenReturn(false)
// your test logic
}

I suspect that the source of the evil here is field injection.
Olvier Gierke (now Drotbohm) wrote a blog post about why field injection is evil.
If you can switch to constructor injection you can mock the service just in your test and pass the mock to the class you want to test.
I just want to leave this answer here as a suggestion for others who might have the chance to use constructor injection instead.

Related

How to mock a service and all the autowired fields in the service?

I am looking to Mock my service and all the autowired fields in it. Below is the service:
#Service
public class MyServiceImpl implements MyService {
#Autowired
#Qualifier("propvalues")
Map<String,String> propvalues;
...
...
}
Below is the Configuration class
#Configuration
public class MyValuesConfig {
#Bean(name = "propvalues")
#ConfigurationProperties(prefix = "mysvcvalues")
private Map<String,String> propvalues;
}
The configuration class reads from application-myvalues.yml
This is my Test class MyServiceImplTest
#ExtendWith(MockitoExtension.class)
public class MyServiceImplTest {
#Mock
private MyService myService;
...
...
}
Now I find that the autowired fields in the mock service is null. Ideally I would want to see the values loaded from the configuration in the Map in the mock service.
Instead of #Mock user #SpyBean
#SpyBean inject object like an actual bean but you can mock any of its parts separately.
Or you can use spy to initialize the object and mock the other parts that you want.
Something like:
Mokito.spy(myService).when(....).thenReturn(...)

Can't inject Bean (child of #Spy injected bean) in my Test

When I'm making a Test, I can't get injected a property of one of the injected beans (with #Spy). I am using Mockito to test.
I tried using #Mock, #Spy, #SpyBean and #InjectMocks in this Bean on my test but I can't get it injected.
#RunWith(MockitoJUnitRunner.class)
public class MyTest{
#InjectMocks private MyService = new myService();
#Spy private MyFirtsDepen firstDepen;
#Autowired #Spy private ChildDepen childDepen;
... More mocks and tests
}
#Service
public class MyService {
#Autowired private MyFirstDepen firstDepen;
....
}
#Mapper
public class MyFirstDepen {
#Autowired private ChildDepen childDepen;
....
}
#Component
public class ChildDepen {
...
}
When my test use firstDepen is working great, but when firstDepen uses childDepend always get Nullpointer. How can I inject this property in my test?
Since your MyFirtsDepen is a mock, there is no way to inject anything to it. Configure mock to return another mock.
when(firstDepen.getChildDepen()).doReturn(childDepen);

Spring Boot, Mockito, injecting mock into scope session bean

I'm having a problem injecting mock into one class I need for testing. I'm trying to mock a Dao class and had no problem doing so using ReflectionTestUtils in various services I'm using, however this one just does not want to work, it keeps calling the Dao class and getting errors from the database.
This is the test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class DedicationControllerTest extends AbstractRestTest {
#Mock
UserDaoImpl userDao;
#Autowired
#InjectMocks
GrantedAuthoritiesLevelsHolder grantedAuthoritiesLevelsHolder;
#Test
public void shouldTest() throws Exception {
//given
String json = this.getJsonFromFile("json/my.json");
Mockito.when(userDao.getUser(Mockito.anyString())).thenReturn(new User(1l, "mock"));
ReflectionTestUtils.setField(grantedAuthoritiesLevelsHolder, "userDao", userDao);
ResultActions result = mockMvc.perform(post( controllerUrl + "/action")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json));
// then
result
.andExpect(status().isOk());
}
}
And this is the class I'm trying to inject mock into:
#Component
#Scope(value="session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class GrantedAuthoritiesLevelsHolder {
#Autowired
private UserDao userDao;
// some methods
}
You will have to register mocked bean as UserDao when the context is getting loaded. You can register it as shown below. Put this in any class annotated with #Configuration
#Bean
#Primary
public UserDao UserDao() {
return mock(UserDao.class);
}
I believe that your configuration may be not enough to put a mock into Spring context.
My advice:
#MockBean(answer=Answers.RETURNS_SMART_NULLS)
UserDao userDao;
#Autowired
GrantedAuthoritiesLevelsHolder grantedAuthoritiesLevelsHolder;
It should put a mock into Spring context, moreover it should give you hints with incorrect/missing stubbing.

Not able to use #Autowired with #Mock while creating unit test cases

I have a service say MainService and it has few managers which are initialised with #Autowired, and it is using some external service which are also #Autowired.
My purpose is to create unit test cases so that I can access the inmemory DB with managers, and want to mock the external service.
Now problem which I am facing is if I use #Autowired in my unit test and use #Mock for external services, then it doesn't use mock methods, Instead it uses the actual implementation. If I do #InjectMocks then it doesn't pick the data from repo as it doesn't find the respective dependencies for managers, and if I use #Autowired and #InjectMocks together it still not being able to use the Mocks.
Something like this
#Service
public class MainService extends AbstractService
{
#Autowired
Manager1 manager1;
#Autowired
Manager2 manager2;
#Autowired
Manager3 manager3;
#Trace(dispatcher = true)
public void mainMethod(int data)
{
int data1 = manager1.getData(int xyz);\\ getting data from DAO
int data2 = manager1.getData(int xyz);\\ getting data from DAO
int data3 = manager1.getData(int xyz);\\ getting data from
\\External Service
}
}
Now the test case I am writing is
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class TestClass {
#InjectMocks
#Autowired
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
Manager3 manager3;
#Before
public void initTest()
{
MockitoAnnotations.initMocks(this);
int dataFirst=1;
int dataSecond =2;
int dataThird=3;
int dataForMethod=4;
repoManager1.save(dataFirst);
repoManager2.save(dataSecond);
}
#Test
public void testMethod()
{
Mockito.when(manager3.getData(Mockito.anyInt())).thenReturn(dataThird);
service.mainMethod(dataForMethod);
}
}
This is a replication of the actual service, when I debug the test I found that the mock is not being used, its using actual implementation, and when I removed #Autowired from MainService then it only execute the mocked method.
Instead of using Field Injection(using #Autowired on class variables) use Constructor Injection. This way you can initialize your MainService class with some mocks and some real Implementations. Something like this
#Service
public class MainService extends AbstractService
{
private final Manager1 manager1;
private final Manager2 manager2;
private final SomeExternalService externalService;
#Autowired
public MainService(Manager1 manager1, Manager2 manager2, SomeExternalService externalService)
this.manager1= manager1;
this.manager2= manager2;
this.externalService = externalService;
}
........................
}
From your test class don't Autowire MainService. Just Autowire Manager1 and Manager2 and Create Mock for SomeExternalService(and initialize it). And then create instance of MainService using constructor.
public class TestClass {
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
SomeExternalService externalService;
#Before
public void setUp(){
service = new MainService(repoManager1, repoManager2, externalService);
}
}
If you want to use Mockito, you need Annotate your TestClass with #RunWith(MockitoJUnitRunner.class) instead of #RunWith(SpringRunner.class).
Then for the #AutoWired in the TestClass for repoManager1, repoManger2. Annotate them with #Mock instead of #Autowired, as you want to mock them with Mockito.
I have not used the SpringRunner myself, but from a quick read, I can see that it is used mostly for Integration Test, where you want to load the SpringContext..etc.
Also #ActiveProfiles("test") is mostly used for Integration Test, where you want to load the spring context, with the 'test' profile properties.

Context initialization issue for controller Unit testing with Spring 3.2 and Mockito

I'm trying to provide a clean Unit Test for a Controller of mine. This Controller has a Service as dependency and this Serviceh has a Datasource as dependency.
The test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ContentActionWebServiceControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private MyService myService;
#Test
public void getRequestActionList() throws Exception {
when(...)
perform(...);
verify(...);
}
#Configuration
#ImportResource("...")
static class MyTestConfiguration {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
And the MyService is something like
#Service
public class MyService {
#Autowired
private MyDataSource myDatasource;
...
}
Because MyService as an Autowired property MyDataSource, the context isn't initialized because it doesn't find any MyDataSource type for satisfying the #Autowired annotation of MyService. But why does it ever try to resolve this annotation? Is this is a mock?
Mockito does use cglib to create a new child class of MyService (and override all methods with mock methods).
But still, the dependencies of the parent will be injected, because this is how Spring does it's job:
if you have a parent class with some #Autowired fields, and a child class that inherits from this parent class, then Spring will inject the #Autowired fields of the parent when instantiating the child. I guess it's the same behavior in your case.
If you use an interface for MyService, then your problem will be solved.
If it's supposed to be a unit test (and not an integration test) you don't even need to use Spring, you can do it all with JUnit+Mockito. Rather than #Autowireing dependencies from Spring context, you can simply create mocks of the support objects (via #Mock) and inject them to the testee (via #InjectMocks). I believe your code could be simplified to something (conceptually) like this:
#RunWith(MockitoJUnitRunner.class)
public class ContentActionWebServiceControllerTest {
#Mock
private Service mockServiceUsedByController;
#InjectMocks
private YourController testee;
#Test
public void getRequestActionList() throws Exception {
assertFalse(testee.getRequestActionList().isEmpty());
// etc.
}
}

Categories