Spring Boot, Mockito, injecting mock into scope session bean - java

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.

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(...)

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

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.

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 #Autowired constructor causes #Value to return null when instantiated in test class

I'm using an autowired constructor in a service that when instantiated in the test class causes the #Value annotations to return null. Autowiring the dependencies directly solves the problem but the project follows the convention of using constructor based autowiring. My understanding is that instantiating the service in the test class is not creating it from the Spring IoC container which causes #Value to return null. Is there a way to create the service from the IoC container using constructor based autowiring without having to directly access the application context?
Example Service:
#Component
public class UpdateService {
#Value("${update.success.table}")
private String successTable;
#Value("${update.failed.table}")
private String failedTable;
private UserService userService
#Autowired
public UpdateService(UserService userService) {
this.userService = userService;
}
}
Example Test Service:
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class})
#WebAppConfiguration
public class UpdateServiceTest {
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService = new UpdateService(mockUserService);
}
}
To make #Value work updateService should be inside of spring context.
The best practice for spring framework integration tests is to include application context in test context and autowiring test source in test:
...
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
...
Mock userService
Option with changing userService to protected and considering that test and source classes are in same package.
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService.userService = mockUserService;
}
Option with reflection with Whitebox:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Whitebox.setInternalState(updateService, 'userService', mockUserService);
}
The #Value is filled by a property placeholder configurer which is a post processor in the spring context. As your UpdateService is not part of the context it is not processed.
Your setup looks a little like a unclear mixture of unit and integration test. For a unit tests you will not need a spring context at all . Simply make the #Value annotated members package protected and set them or use ReflectionTestUtils.setField() (both shown):
public class UpdateServiceTest {
#InjectMocks
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(updateService, "successTable", "my_success");
updateService.failedTable = "my_failures";
}
}
For an integration test all wiring should be done by spring.
For this I added a inner config class providing the mock user service (the #Primary is only for the case you have any other user service in your context) and the mock is stored in a static member here to have simple access to the mock from the tests afterwards.
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class, UpdateServiceTest.TestAddOn.class})
#WebAppConfiguration
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
private static UserService mockUserService;
static class TestAddOn {
#Bean
#Primary
UserService updateService() {
mockUserService = Mockito.mock(UserService.class);
return mockUserService;
}
}
}

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