I'm trying to test my Spring 5 web controllers with JUnit 5.
The two way to test controller (as mentionned in spring documentation) always give me null pointer.
This is my test class
import com.lacunasaurus.gamesexplorer.test.configuration.TestBackEndConfiguration;
import com.lacunasaurus.gamesexplorer.test.configuration.TestWebConfig;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
#RunWith(JUnitPlatform.class)
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
public class TestAuthenticationCreateAccountController {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
// this.mockMvc = MockMvcBuilders.standaloneSetup(new AuthenticationCreateAccountController()).build();
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getAccount() throws Exception {
// Here i've got an null pointer
mockMvc.perform(get("/"));
}
}
Here my web configuration for tests
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#ComponentScan(basePackages = {"com.lacunasaurus.gamesexplorer.web"})
public class TestWebConfig implements WebMvcConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And now my controller
#Controller
#RequestMapping("/create-account")
public class AuthenticationCreateAccountController {
#Autowired
UserAccountValidator accountValidator;
#Autowired
AuthenticationService authenticationService;
#GetMapping
public String navigate() {
return "authentication/create-account";
}
#ModelAttribute("userAccount")
public UserAccount setDefaultAccount() {
return new UserAccount();
}
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(accountValidator);
}
#PostMapping
public String createAccount(#Validated UserAccount userAccount, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "authentication/create-account";
}
authenticationService.createUserAccount(userAccount);
return "authentication/create-account";
}
}
EDIT : The stacktrace given by the IDE
java.lang.NullPointerException at com.lacunasaurus.gamesexplorer.test.controller.TestAuthenticationCreateAccountController.getAccount(TestAuthenticationCreateAccountController.java:41)
Results :
Tests in error:
TestAuthenticationCreateAccountController.getAccount:41 NullPointer
I've got already test my backend with junit 5 and spring and everything work well.
Thanks to thoses who will help me to understand how to test controller :)
The new test for controllers :
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
#TestInstance(Lifecycle.PER_CLASS)
public class TestAuthenticationCreateAccountController {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
void getAccount() throws Exception {
mockMvc.perform(get("/toto")).andExpect(status().isOk());
}
}
If you're using Junit5, See this reference
#ExtendWith(MockitoExtension.class) // For Junit5
public class TestAuthenticationCreateAccountController {
#Mock
private WebApplicationContext wac;
#InjectMocks
private AuthenticationCreateAccountController ac;
private MockMvc mockMvc;
#BeforeEach // For Junit5
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(ac).build();
}
#Test
public void getAccount() throws Exception {
mockMvc.perform(get("/"));
}
}
In order to use JUnit Jupiter, you must use autoconfigure MockMvc. This is a help to resolve all related configuration.
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#AutoConfigureMockMvc
#Autowired
private MockMvc mockMvc;
This should fix your issue.
Related
I am trying to do an example to change the xml config to Java config for Spring MVC. But my simple example is not working. On running this project on server, I can't see any beans initializing or the dispatcher servlet name on console.
and I get 404 error on running the http://localhost:8080/Servlet3Example/
I have created a maven project and following is my code:
package com.project.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyDispatcherServlet extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("get root config");
//return new Class[]{RootConfig.class};
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("get web config");
return new Class[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
System.out.println("in dispatcher servlet");
return new String[] {"/"};
}
}
And the WebConfig is:
package com.project.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages={"com.project.controllers"})
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setSuffix(".jsp");
viewResolver.setPrefix("/WEB-INF/views/");
return viewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
And controller:
package com.project.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#RequestMapping(value="/",method=RequestMethod.GET)
public String home()
{
return "home";
}
}
Can you please change #ComponentScan to #ComponentScan(basePackages={"com.project.*"})
I am trying to configure a Spring based app, where I want to configure two view resolvers. From my controller, if I return just the string name like "login", then it should be handled by the Thymeleaf resolver, whereas if the controller's method returns an object, then appropriate json view should be used. When I try to run my application as configured below, I get the following error
"Could not resolve view with name 'login' in servlet with name
'dispatcher'"
Requesting you guys to look at the Java classes below. The first is the configuration class, the second is the Controller I am trying to use.
package com.gojha.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public ViewResolver viewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
return cnvr;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public TemplateResolver templateResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine((SpringTemplateEngine) templateEngine());
return viewResolver;
}
}
Controller
package com.gojha.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
#Controller
#RequestMapping("/")
public class LoginController {
private RestTemplate restTemplate;
private class Test {
private String a;
public Test() {
super();
}
public Test(String a) {
super();
this.a = a;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
#Autowired
public LoginController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#RequestMapping(method=GET)
public String testing(){
return "login";
}
#RequestMapping(method=GET, produces="application/json")
public Test testing2(){
return new Test("wow");
}
}
I hope the code is self-explanatory.
I got it working by changing the configuration file and allocating orders to view resolvers. From what I understand, it looks like first it tries to resolve the view using ContentNegotiation, and if it fails, falls back to Thymeleaf resolver. I am marking this as the answer, if someone has a better approach, or a suggested correction, let me know.
package com.gojha.web;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public ViewResolver viewResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
viewResolver.setOrder(2);
return viewResolver;
}
#Bean
public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
cnvr.setOrder(1);
List<View> views = new ArrayList<View>();
views.add(jsonView());
cnvr.setDefaultViews(views);
return cnvr;
}
#Bean
public View jsonView() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
}
}
I am unable to get WebApplicationContext inside ServletContextListener. I searched for solution but non of them are working for me. I would appreciate if any one can help. Thanks in advance.
Below are code snippets-
My AppConfig class:
package in.andonsystem.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"in.andonsystem"})
public class AppConfig extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
My AppInitializer Class.
package in.andonsystem.config;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class Appinitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
rootContext.setServletContext(servletContext);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
rootContext.refresh();
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
My ContextListener Class:
package in.andonsystem.config;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
#WebListener
public class ContextListener implements ServletContextListener{
private Logger logger = Logger.getLogger("ContextListener");
#Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("contextInitialized()");
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
System.out.println("Is rootContext null:" + (rootContext == null));
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
stdout output is always coming true.
What am I missing?
Finally came up with the solution. The problem was that ServletContextListener was being loaded before ContextLoaderListener and Therefore a WebApplicationContext returned was null because ContextLoaderListener must be loaded before ServletContextListener in order to get the rootContext.
I removed #WebListener annotation from my ContextListener and added this Listener Programatically in onStartup method of AppInitializer after Adding ContextLoaderListener.
Now my ContextListener without #WebListener annotation is:
public class ContextListener implements ServletContextListener{
private Logger logger = Logger.getLogger("ContextListener");
#Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("contextInitialized()");
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
System.out.println("Is rootContext null:" + (rootContext == null));
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("contextDestroyed()");
}
}
and AppInitializer is :
public class Appinitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
rootContext.setServletContext(servletContext);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
//ContextListener must be added after ContextLoaderListener
servletContext.addListener(ContextListener.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Looks basically correct, but I think you should remove the following 2 lines from your initializer class:
rootContext.setServletContext(servletContext);
and
rootContext.refresh();
I'm new to Mockito and trying to test my service layer. My DAO layer is #Autowired in service and Hibernate is also autowired. Hibernate is not loading while testing. I always get NullPointerException. Here is my code:
EmployeeService (Interface)
package com.spring.crud.service;
import java.util.List;
import com.spring.crud.entity.Employee;
public interface EmployeeService {
Employee save(Employee employee);
boolean update(Employee employee);
Employee find(Integer id);
List<Employee> getEmployees();
boolean remove(Integer id);
}
EmployeeServiceImpl (Class)
package com.spring.crud.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.spring.crud.dao.EmployeeDAO;
import com.spring.crud.entity.Employee;
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
private EmployeeDAO dao;
public Employee save(Employee employee) {
return dao.save(employee);
}
public boolean update(Employee employee) {
return dao.update(employee);
}
public Employee find(Integer id) {
return dao.find(id);
}
public List<Employee> getEmployees() {
return dao.getEmployees();
}
public boolean remove(Integer id) {
return dao.remove(id);
}
}
EmployeeDAO (Interface)
package com.spring.crud.dao;
import java.util.List;
import com.spring.crud.entity.Employee;
public interface EmployeeDAO {
Employee save(Employee employee);
boolean update(Employee employee);
Employee find(Integer id);
List<Employee> getEmployees();
boolean remove(Integer id);
}
EmployeeDAOImpl (Class)
package com.spring.crud.dao;
import java.util.List;
import javax.transaction.Transactional;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.stereotype.Repository;
import com.spring.crud.entity.Employee;
#Transactional
#Repository
public class EmployeeDAOImpl extends HibernateUtil implements EmployeeDAO{
public Employee save(Employee employee) {
Session session = getCurrentSession();
session.save(employee);
return employee;
}
public boolean update(Employee employee) {
Session session = getCurrentSession();
session.update(employee);
return false;
}
public Employee find(Integer id) {
Session session = getCurrentSession();
Employee employee = (Employee)session.get(Employee.class, id);
return employee;
}
public List<Employee> getEmployees() {
Session session = getCurrentSession();
Query query = session.createQuery("from Employee");
#SuppressWarnings("unchecked")
List<Employee> employees = (List<Employee>)query.list();
return employees;
}
public boolean remove(Integer id) {
Session session = getCurrentSession();
Employee employee = (Employee)session.get(Employee.class, id);
if(employee!=null){
session.delete(employee);
return true;
}
return false;
}
}
HibernateUtil
package com.spring.crud.dao;
import javax.annotation.PostConstruct;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class HibernateUtil extends HibernateDaoSupport{
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void init() {
setSessionFactory(sessionFactory);
}
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
EmployeeServiceTest (Test class)
package com.spring.crud.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.test.context.ContextConfiguration;
import com.spring.crud.config.WebConfig;
import com.spring.crud.dao.EmployeeDAO;
import com.spring.crud.dao.EmployeeDAOImpl;
import com.spring.crud.entity.Employee;
import com.spring.crud.service.EmployeeService;
import com.spring.crud.service.EmployeeServiceImpl;
#ContextConfiguration(classes = {WebConfig.class})
public class EmployeeServiceTest {
private EmployeeDAO employeeDAO;
private EmployeeService employeeService = new EmployeeServiceImpl();
#Spy
List<Employee> employees = new ArrayList<Employee>();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
employeeDAO = mock(EmployeeDAOImpl.class);
}
#Test
public void listEmployees() {
}
#Test
public void create() {
Employee employee = new Employee();
employee.setDateOfBirth(new Date());
employee.setGender("male");
employee.setName("Ashutosh");
when(employeeDAO.save(any(Employee.class)))
.thenAnswer(new Answer<Employee>() {
public Employee answer(InvocationOnMock invocation) throws Throwable {
Employee employee = (Employee) invocation.getArguments()[0];
employee.setId(1);
return employee;
}
});
assertNull(employee.getId());
employee = employeeService.save(employee);
assertNotNull(employee.getId());
assertTrue(employee.getId()>0);
}
#Test
public void edit() {
}
#Test
public void update() {
}
#Test
public void remove() {
}
}
I can't find much on this on the internet.
Just because you create a mock employee DAO in your test doesn't mean that your service will use it. It won't. When you do
new EmployeeServiceImpl();
you create an instance of the service, and its DAO field is left uninitialized (so null).
Use constructor injection, and pass the mock DAO to the service constructor:
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeDAO dao;
#Autowired
public EmployeeServiceImpl(EmployeeDAO dao) {
this.dao = dao;
}
...
}
And/or at least use Mockito annotations correctly:
#Mock
private EmployeeDAO employeeDAO;
#InjectMocks
private EmployeeServiceImpl employeeService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
OK, I found some fixes and now the test runs.
First, I fixed the HibernateUtil
package com.spring.crud.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class HibernateUtil{
#Autowired
private SessionFactory sessionFactory;
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
This is the EmployeeServiceTest class
package com.spring.crud.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.test.context.ContextConfiguration;
import com.spring.crud.config.WebConfig;
import com.spring.crud.dao.EmployeeDAO;
import com.spring.crud.entity.Employee;
import com.spring.crud.service.EmployeeServiceImpl;
#ContextConfiguration(classes = {WebConfig.class})
public class EmployeeServiceTest {
#Mock
private EmployeeDAO employeeDAO;
#InjectMocks
private EmployeeServiceImpl employeeService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void listEmployees() {
}
#Test
public void create() {
Employee employee = new Employee();
employee.setDateOfBirth(new Date());
employee.setGender("male");
employee.setName("Ashutosh");
when(employeeDAO.save(any(Employee.class)))
.thenAnswer(new Answer<Employee>() {
public Employee answer(InvocationOnMock invocation) throws Throwable {
Employee employee = (Employee) invocation.getArguments()[0];
employee.setId(10);
return employee;
}
});
assertNull(employee.getId());
employee = employeeService.save(employee);
System.out.println("Id = " + employee.getId());
assertNotNull(employee);
assertEquals((Integer)10, (Integer)employee.getId());
}
#Test
public void edit() {
}
#Test
public void update() {
}
#Test
public void remove() {
}
}
I am trying to create a JUnit test case for spring mvc rest controller and service that is accessed by controller.
I am using Mockito for doing the above. I'm am able to successfully invoke the mock injected controller from the test case and also when I debug I see that the mocked userService is available in the mocked contoller, but the method within the service object is not getting invoked. While debugging I observe that it just steps over the service method call.
I do not see any kind of exception too.
I am using
Maven 3
Spring 4.1.1 Release version
Junit 4.11
Java version 1.6
I have pasted my code below:
1. Test Class
package controller;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/spring/application-config.xml")
public class UserControllerTest {
private MockMvc mockMvc;
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testRegisterUser()throws Exception{
User user = new User();
user.setCity("City");
user.setTown("Town");
user.setFirstname("Name");
user.setLastname("LastName");
user.setPassword("abc#123");
user.setUsername("abc#gmail.com");
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(object);
}
}
2. Controller under Test
package com.myPackage.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RestController
#RequestMapping("/service/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
#Autowired
private IUserService userService;
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/register/", method = RequestMethod.POST,headers="Accept=application/json")
public String registerUser(#RequestBody User user) {
logger.info("register user:"+user.toString());
String userDetails = userService.registerUser(user);
logger.info("user registered:"+userDetails);
return userDetails;
}
}
3. Service to be invoked within controller in test
The service method registerUser is to be invoked from controller above. I do not see any logs getting printed on the console when we run the test case. Also when debugging I see that userSerivice instance of type like this - IUserService$$EnhancerByMockitoWithCGLIB$$c00081eb is created but when I dig deep all to see the list of methods under mockhandlers registered methods, I only see 'toString' method in the list of invocations. Not sure if this gives some indication on why this method 'registerUser' in the service class below is not getting invoked during test case.
package com.myPackage.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import com.myPackage.dao.IUserDao;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
#Service("userService")
public class UserService implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
#Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
#Override
public String registerUser(User user) {
// TODO Auto-generated method stub
logger.info("New User for registration:",user);
if(user!=null && user.getUsername()!=null){
User alreadyExist = userDao.getUserByLogin(user.getUsername());
if(alreadyExist!=null){
return "userAlreadyExists";
}
}
logger.info("New User for registration complete:",user.getUser_id());
return null;
}
}
4. Interface implemented by UserService class in point 3 above
IUserService. The mocked userService in test class above is of type IUserService.
package com.myPackage.service;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
public interface IUserService {
public String registerUser(User user);
}
Your method (read code) within userService will never actually be invoked from your controller because, it has been mocked out by mockito.
You have defined this yourself by doing:
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
MockitoAnnotations.initMocks(this);
If you want to assert that the method has been called you can using,
verify(userService, times(1)).registerUser(any(User.class));
#Jonas: Thanks for your insight. If i don't mock userService then at the time of unit test, userService is found null under userController method matching registerUser. And so after reading somewhere i mocked userService. Also after doing the changes as suggested by you, the mocked service method still does not get invoked - Below are my changes -
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
If you want to test the actual service method, then autowire it:
#Autowired
private IUserService userService;
If you want to use a mock like you're doing now, you need to stub the method:
when(userService.registerUser(any(User.class))).thenReturn("expected string");