I'm facing problems mocking services injected inside of other services within the Spring framework. Here is my code:
#Service("productService")
public class ProductServiceImpl implements ProductService {
#Autowired
private ClientService clientService;
public void doSomething(Long clientId) {
Client client = clientService.getById(clientId);
// do something
}
}
I want to mock the ClientService inside my test, so I tried the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/spring-config.xml" })
public class ProductServiceTest {
#Autowired
private ProductService productService;
#Mock
private ClientService clientService;
#Test
public void testDoSomething() throws Exception {
when(clientService.getById(anyLong()))
.thenReturn(this.generateClient());
/* when I call this method, I want the clientService
* inside productService to be the mock that one I mocked
* in this test, but instead, it is injecting the Spring
* proxy version of clientService, not my mock.. :(
*/
productService.doSomething(new Long(1));
}
#Before
public void beforeTests() throws Exception {
MockitoAnnotations.initMocks(this);
}
private Client generateClient() {
Client client = new Client();
client.setName("Foo");
return client;
}
}
The clientService inside productService is the Spring proxy version, not the mock that I want. Is it possible to do what I want with Mockito?
You need to annotate ProductService with #InjectMocks:
#Autowired
#InjectMocks
private ProductService productService;
This will inject the ClientService mock into your ProductService.
There are more ways to achieve this, the most easy way to do this will be don't use field injection, but setter injection which means you should have:
#Autowired
public void setClientService(ClientService clientService){...}
in your service class, then you can inject your mock to the service in test class:
#Before
public void setUp() throws Exception {
productService.setClientService(mock);
}
important: If this is only a unit test, please consider don't use SpringJUnit4ClassRunner.class, but MockitoJunitRunner.class, so that you can also use field inject for your fields.
In addition to
#Autowired
#InjectMocks
private ProductService productService;
Add the following method
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
I would like to suggest you annotate the Test target with #InjectMock
Currently
#Autowired
private ProductService productService;
#Mock
private ClientService clientService;
Change to
#InjectMock
private ProductService productService;
#Mock
private ClientService clientService;
incase you still have NullPointerException for the MockingService => you can use Mockito.any() as arguments.
Hopefully, it will help you.
Related
I'm developing a small application in Java using Spring, so I have this Service:
public class AccountService implements UserDetailsService {
#Autowired
private AccountRepository accountRepository;
#Autowired
private BlogRepository blogRepository;
#Autowired
private ImageService imageService;
#PostConstruct
protected void initialize() throws IOException {
Account user = new Account("user", "demo", "ROLE_USER");
save(user);
Blog userBlog = new Blog("userBlog", true, user);
userBlog.setAvatar(imageService.createBlogAvatar(userBlog.getName()));
blogRepository.save(userBlog);
save(new Account("admin", "admin", "ROLE_ADMIN"));
}
// More methods
}
And this test:
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
#InjectMocks
private AccountService accountService = new AccountService();
#Mock
private AccountRepository accountRepositoryMock;
#Test
public void shouldInitializeWithTwoDemoUsers() throws IOException {
// act
accountService.initialize();
// assert
verify(accountRepositoryMock, times(2)).save(any(Account.class));
}
}
Why when I run the tests I get this exception?
shouldInitializeWithTwoDemoUsers(es.udc.fi.dc.fd.account.AccountServiceTest) Time elapsed: 0.016 sec <<< ERROR!
java.lang.NullPointerException: null
at es.udc.fi.dc.fd.account.AccountService.initialize(AccountService.java:45)
at es.udc.fi.dc.fd.account.AccountServiceTest.shouldInitializeWithTwoDemoUsers(AccountServiceTest.java:42)
Using the #PostConstruct annotation it's supposed to have all beans injected right?
Few things here. First of all #InjectMocks generally makes things easier but Mockito not a dependency injection framework, so its not guaranteed to work properly.
Secondly, for #InjectMocks to work properly you need to #Mock all your objects as well and not manually create the class you are trying to inject. I don't believe its the case anymore but in order versions of mockito, the order of the #Mocks would matter as well.
This code might work for you
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
#Mock
private AccountRepository accountRepositoryMock;
#Mock
private BlogRepository blogRepository;
#Mock
private ImageService imageService;
#InjectMocks
private AccountService accountService ;
#Test
public void shouldInitializeWithTwoDemoUsers() throws IOException {
// act
accountService.initialize();
// assert
verify(accountRepositoryMock, times(2)).save(any(Account.class));
}
}
You need to mock all the dependencies that your test subject is using. You may want to do this in your AccountServiceTest class:
#Mock
private BlogRepository blogRepositoryMock;
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;
}
}
}
In my class, dependencies are injected by Spring. During testing, I am using mocks. I am getting null pointer exception when I call sys.getId("abc12345") in the following code. I am wondering how to write a unit test that gets 100% coverage.
Class under test:
public class SystemUT implements SUTIface{
#Inject
private AccountLookupDAO dao;
#Inject
private OrchService service;
public Response perform(Request req){
String sellerId = getId(request.getSeller().getNum());
String buyerId = null;
if(req.getBuyerId){
buyerId = getId(request.getBuyer().getNum())
}
service.execute(Request,sellerId,buyerId)
}
String getId(String num){
PrefAcct prefAcctObj = dao.lookupPrefId(num,Consants.StrArr);
PrefSysOfRecObj sorObj= prefAcctObj.getSysOfRecord();
return sorObj.getId();
}
}
Unit test:
public Class SystemUTTest{
#Mock
SystemUT sys;
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
#Mock
PrefAcct prefAcctObj;
#Mock
PrefSysOfRecObj sorObj;
#Before
public void setup(){
Whitebox.setInternalState(sys, daoMock, serviceMock);
}
#Test
public test getId(){
when(dao.lookupPrefId(any(String.class), any(String[].class))).thenReturn(prefAcctObj);
when(prefAcctObj.getSysOfRecord()).thenReturn(sorObj);
when(sorObj.getId()).thenReturn("185");
assertEquals("185",sys.getId("abc12345"));
}
}
Your problem is that your SystemUT class doesn't have its dependencies injected. You could have Spring do this by using their JUnitRunner, but it's not really a unit test then, since you'd be letting Spring dictate which dependencies get injected. Really, you want to control them, and one way to do that is to transform your class to expose its dependencies via a constructor:
public class SystemUT implements SUTIface{
private final AccountLookupDAO dao;
private final OrchService service;
#Inject
public SystemUT(AccountLookupDAO dao, OrchService service) {
this.dao = dao;
this.service = service;
}
}
This will function identically to your current approach since Spring is able to inject dependencies using a constructor annotated with #Inject. Now, when you instantiate your SystemUT class for test, pass mocked objects for its dependencies:
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
private SystemUT sys;
#Before
public void setup(){
sys = new SystemUT(daoMock, serviceMock);
Whitebox.setInternalState(sys, daoMock, serviceMock);
}
I want to test a class using Spring + JUnit + Mockito but I don't manage to make it work properly.
Let's say my class references a Service:
#Controller
public class MyController
{
#Autowired
private MyService service;
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
And this Service references a Repository:
#Service
public class MyService {
#Autowired
private MyRepository repository;
public void whatever() {}
public void create() {
repository.save();
}
}
When testing the MyController class, I want the service to be mocked. The problem is: even when the service is mocked, Spring tries to inject the repository in the mock.
Here is what I did. Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {
#Autowired
private MyController myController;
#Test
public void testDoSomething() {
myController.doSomething();
}
}
Configuration class:
#Configuration
public class MyControllerTestConfiguration {
#Bean
public MyController myController() {
return new MyController();
}
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
And the error I get: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency
I tried to initialize the mock using Mockito's #InjectMocks annotation but this fails because the #PostConstruct method is called before the mocks injection, generating a NullPointerException.
And I cannot simply mock the repository because in real life that would make me mock A LOT of classes...
Can anyone help me on this?
Use constructor instead of field injection. That makes testing a lot easier.
#Service
public class MyService {
private final MyRepository repository;
#Autowired
public MyService(MyRepository repository) {
this.repository = repository;
}
public void whatever() {}
public void create() {
repository.save();
}
}
-
#Controller
public class MyController {
private final MyService service;
#Autowired
public MyController(MyService service) {
this.service = service;
}
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
This has several advantages:
You don't need Spring in your tests. This allows you to do proper unit tests. It also makes the test incredibly fast (from seconds to milliseconds).
You cannot accidentally create an instance of a class without its dependencies which would result in a NullPointerException.
As #NamshubWriter pointed out:
[The instance fields for the dependencies] can be final, so 1) they cannot be accidentally modified, and 2) any thread reading the field will read the same value.
Discard the #Configuration class and write a test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#Mock
private MyRepository repository;
#InjectMocks
private MyService service;
#Test
public void testDoSomething() {
MyController myController = new MyController(service);
myController.doSomething();
}
}
Use interfaces, especially if you use some kind of AOP (transactions, security, etc), i.e. you'll have interface MyService and class MyServiceImpl.
In configuration you'll have:
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
you should put the #InjectMocks annotation in your controller and #Mock in your service, look:
#Autowired
#InjectMocks
private MyController myController;
#Autowired
#Mock
private MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDoSomething() {
myController.doSomething();
}
I'm trying to unit test a Spring 4.0.0 MVC application.
My controller is defined as follow:
#Controller
#RequestMapping("/test")
public class TestCtrl {
#Autowired
private TestService testService;
#Autowired
private TestRessourceAssembler testRessourceAssembler;
#Autowired
private ResponseComposer responseComposer;
#RequestMapping(value = "", method = RequestMethod.GET,produces = "application/json")
public HttpEntity showAll(Pageable pageable) {
Page<Test> patr = testService.getAll(pageable);
return responseComposer.composePage(patr,testRessourceAssembler);
}
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public HttpEntity<TestRessource> show(#PathVariable String name) {
Test test = testService.getOne(name);
if(test == null){
return new ResponseEntity("Erreur !",HttpStatus.NOT_FOUND);
}
return responseComposer.compose(test,testRessourceAssembler);
}
}
My controller unit test is as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#WebAppConfiguration
#ContextConfiguration(classes = {ApplicationConfig.class, TestMongoConfig.class, RestConfig.class, WebMvcConfig.class})
public class TestCtrlTests{
#InjectMocks
TestCtrl testCtrl;
#Mock
TestService testService;
#Autowired
protected WebApplicationContext wac;
protected MockMvc mockMvc;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
when(testService.getOne("jexiste")).thenReturn(new com.thalesgroup.ito.c2s.mc.portail.test.domain.Test("jexiste",1990));
when(testService.getOne("plaf")).thenReturn(null);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void simpleGetAnswer() throws Exception{
assertNotNull(mockMvc);
mockMvc.perform(get("/test")).andExpect(status().isOk());
mockMvc.perform(get("/test/jexiste")).andExpect(status().isOk());
mockMvc.perform(get("/test/plaf")).andExpect(status().isNotFound());
}
}
When I'm running the test, the "normal" TestService bean is injected and used (I can see the trace in the log), not the mock.
So I read some things on the internet and replaced
this.mockMvc = webAppContextSetup(this.wac).build();
with
this.mockMvc = standaloneSetup(TestCtrl.class).build();
But, and I knew it would happen, I've no more Spring context when doing this, so my PageableArgumentResolver and my other beans (testRessourceAssembler, responseComposer) aren't injected anymore... So they are Null and happen a NullPointerException.
My question is:
1) I'm I designing something wrong ?
2) If not, how can I inject a mock in my controller while keeping other beans from the context ?
Thanks to you !
I'm looked into your tests and this should work. Simply build your MockMvc on your controller with mocked beans. After this all mocks will be visible inside test.
A MockMvcBuilder that accepts #Controller registrations thus allowing full control over the instantiation and the initialization of controllers and their dependencies similar to plain unit tests, and also making it possible to test one controller at a time.
Don't use Spring Integration test! This is simple unit testing!
Fixed test
#RunWith(MockitoJUnitRunner.class)
public class TestCtrlTests{
#InjectMocks
TestCtrl testCtrl;
#Mock
TestService testService;
protected MockMvc mockMvc;
#Before
public void setup(){
when(testService.getOne("jexiste")).thenReturn(new com.thalesgroup.ito.c2s.mc.portail.test.domain.Test("jexiste",1990));
when(testService.getOne("plaf")).thenReturn(null);
this.mockMvc = standaloneSetup(testCtrl).build();
}
#Test
public void simpleGetAnswer() throws Exception{
assertNotNull(mockMvc);
mockMvc.perform(get("/test")).andExpect(status().isOk());
mockMvc.perform(get("/test/jexiste")).andExpect(status().isOk());
mockMvc.perform(get("/test/plaf")).andExpect(status().isNotFound());
}
}