recently when i do my work using spring-mvc-test-framework,code like this:
this.mockMvc.perform(MockMvcRequestBuilders.get("/manage/bill/detail/{id}","5507c2240cf2bc43ba2367bd").accept(MediaType.ALL)).andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcRestDocumentation.document("index"));
then i got this error.
java.lang.IllegalArgumentException: No InputStream specified
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.util.FileCopyUtils.copy(FileCopyUtils.java:106)
at org.springframework.util.FileCopyUtils.copyToByteArray(FileCopyUtils.java:156)
at org.springframework.restdocs.mockmvc.MockMvcOperationRequestFactory.createOperationRequest(MockMvcOperationRequestFactory.java:83)
at org.springframework.restdocs.mockmvc.RestDocumentationResultHandler.handle(RestDocumentationResultHandler.java:93)
at org.springframework.test.web.servlet.MockMvc$1.andDo(MockMvc.java:155)
I searched and find no answers.
can anybody tell me why this happens?
Now I got another solution.
actually I use Spring MVC test example in its offical reference http://docs.spring.io/spring-restdocs/docs/1.0.0.RELEASE/reference/html5/#getting-started-build-configuration-maven-packaging and use the setUp() method like this:
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
the aim is to document api 。Now the another solution is I use apply() method and then problem solved.
first,i changed to :
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)).build();
then solved the version problem like this:
spring-restdocs is not recognizing apply()
All my code below:
package com.dream.bwm.test.test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { "classpath:spring/root-context.xml", "classpath:spring/app-context.xml" })
public class DocsGenerator {
public static String outputDir = "D:\\workspace-bwme\\pro-root\\bwme-backend\\target\\generated-snippets";
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation(outputDir);
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
// this.mockMvc =
// MockMvcBuilders.webAppContextSetup(this.context).build();
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)).build();
}
// #Test
public void printOnConsole() throws Exception {
this.mockMvc
.perform(
MockMvcRequestBuilders.get("/manage/bill/detail/{id}", "5507c2240cf2bc43ba2367bd").accept(
MediaType.ALL_VALUE)).andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk());// MockMvcRestDocumentation.document("index")
}
#Test
public void writeToDocument() throws Exception {
this.mockMvc
.perform(
RestDocumentationRequestBuilders.get("/manage/bill/detail/{id}", "5507c2240cf2bc43ba2367bd")
.accept(MediaType.ALL)).andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcRestDocumentation.document("index"));
}
}
hope this can give you some help if someone encounters such questions.
Related
I want to test my spring boot application using TestRestemplate, however I am unable to test get url, what I am trying to do here is I am injecting some predefine objects setting into list and mocking find all method.
below is my test code looks like.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PersonControllerTests {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private PersonRepository mockRepository;
#Before
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
}
#Test
public void getPersonsGoodReq() throws Exception {
ResponseEntity<Person[]> response = restTemplate
.withBasicAuth("admin", "password")
.getForEntity("/persons/all", Person[].class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(response.getBody().length, 2);
}
}
I am expecting answer to be 2, but when I am seeing body response it is empty array.
what might have gone wrong I am unable to get
As you're using JUnit Jupiter, you have to use #BeforeEach. #Before is from JUnit 4 and hence wasn't invoked as part of the test's lifecycle:
#BeforeEach
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
}
I have a bit of trouble with this.
I'm trying to test the web layer of my Spring boot app (with JUnit5).
I'm using the #WebMvcTest(NoteController::class) to allow me to autowire MockMvc in order to mock requests.
But I get the below error :
kotlin.UninitializedPropertyAccessException: lateinit property mvc has not been initialized
NoteControllerTest
import org.hamcrest.Matchers.`is`
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
#ExtendWith(SpringExtension::class)
#WebMvcTest(NoteController::class)
class NoteControllerTest {
#Autowired
private lateinit var mvc: MockMvc
#Test
fun should_create_a_note() {
mvc.perform(
post("/notes"))
.andExpect(status().isCreated)
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.content", `is`("my content")))
}
}
NoteController
import fr.$$.$$.api.CreateNote
import fr.$$.$$.api.FetchNote
import fr.$$.$$.resources.Note
import fr.$$.$$.resources.toResource
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import java.net.URI
#RestController("/notes")
class NoteController(val createNote: CreateNote,
val fetchNote: FetchNote) {
#GetMapping
fun getAllNotes(): ResponseEntity<List<Note>> {
return ResponseEntity(fetchNote.all().toResource(), HttpStatus.OK)
}
#PostMapping
fun createNote(): ResponseEntity<Note> {
val note = createNote.with("my content").toResource()
return ResponseEntity.created(URI("")).body(note)
}
}
SmartNotesApplicationTest
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.test.context.junit.jupiter.SpringExtension
#ExtendWith(SpringExtension::class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
class SmartNotesApplicationTest {
#Test
fun contextLoad() {
}
}
Thanks in advance.
I inject the WebApplicationContext and then build a new MockMvc for each test from that.
#SpringBootTest
class SomeTest {
#Autowired
lateinit var webApplicationContext: WebApplicationContext
lateinit var mockMvc: MockMvc
#BeforeEach
fun beforeEach() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
}
// Tests go here!
}
thanks for the answers, I put the working answer in Java with Spring Boot 2.2.6:
AuthorController.class
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping(path = "/authors")
public class AuthorController {
#GetMapping("/health")
public boolean healthcheck() {
return true;
}
}
AuthorControllerIT.class
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#SpringBootTest
#AutoConfigureMockMvc
class AuthorControllerIT {
#Autowired
private MockMvc mockMvc;
#Test
public void test_web_layer() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/authors/health"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("true"));
}
}
Note :
You can use #WebMvcTest(AuthorController.class) instead of #SpringBootTest + AutoConfigureMockMvc
With this annotation, it will only load the web layer.
If you have dependencies in your controller (services, repositories...) you must use :
#MockBean
private MyService service;
I am testing a spring mvc controller which gets a webservice client autowired and its been mocked vai mockito. But the mocking is not working. Invoking "verify(stuClient, times(1)).getAllStudents(sAndPCommand);" in test returns
Wanted but not invoked:
stuClient.getAllStudents(
com.xyz.crudserviceclient.utilitybeans.SortablePagedCommand#3028e50e
);
-> at com.xyz.controllers.StudentControllerTest.testGetHomePage(StudentControllerTest.java:101)
Actually, there were zero interactions with this mock.
at com.xyz.controllers.StudentControllerTest.testGetHomePage(StudentControllerTest.java:101).....
Below is my controller method in test:
#RequestMapping(value = "/getHomePage.do", method = RequestMethod.GET)
public ModelAndView getHomePage(#RequestParam(value = "first", required = false) Integer first,
#RequestParam(value = "max", required = false) Integer max, #RequestParam(value = "sortBy",
required = false) String sortBy,
#RequestParam(value = "sortDirection", required = false) String sortDir) {
ModelAndView mav = new ModelAndView("home");
SortablePagedCommand sortablePagedCommand = new SortablePagedCommand();
sortablePagedCommand.setFirst(first);
sortablePagedCommand.setMax(max);
sortablePagedCommand.setSort(sortBy);
sortablePagedCommand.setSortDir(sortDir);
PagedResult<StudentBean> students = studentServiceClient.getAllStudents(sortablePagedCommand);
List<StudentBean> studentList = students.getItems();
int noOfRecords = students.getUnfilteredItems();
int noOfPages = (int) Math.ceil(noOfRecords * 1.0 / max);
mav.addObject("sortByField", sortBy);
mav.addObject("sortDirField", sortDir);
mav.addObject("studentList", studentList);
mav.addObject("noOfPages", noOfPages);
mav.addObject("currentPage", first);
return mav;
}
And below is my test Class:
package com.xyz.controllers;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.sql.Date;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
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 com.xyz.crudserviceclient.beans.StudentBean;
import com.xyz.crudserviceclient.client.StudentServiceClient;
import com.xyz.crudserviceclient.utilitybeans.PagedResult;
import com.xyz.crudserviceclient.utilitybeans.SortablePagedCommand;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:WebContent/WEB-INF/dispatcher-servlet.xml" })
#WebAppConfiguration
public class StudentControllerTest {
private MockMvc mockMvc;
#InjectMocks
private StudentController controller;
#Mock
private StudentServiceClient stuClient;
#Autowired
private WebApplicationContext webAppContext;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
Mockito.reset(stuClient);
}
#SuppressWarnings("unchecked")
#Test
public void testGetHomePage() throws Exception {
StudentBean sb1 = new StudentBean();
sb1.setFirstName("Sai");
sb1.setLastName("Palaparthi");
sb1.setGender("Male");
sb1.setDOB(new Date(System.currentTimeMillis()));
sb1.setEmail("v.p#gmail.com");
sb1.setMobileNumber("8121157247");
sb1.setAddress("Huda");
sb1.setCourses(Arrays.asList("Math", "Chem"));
StudentBean sb2 = new StudentBean();
sb2.setFirstName("Viswanath");
sb2.setLastName("Palaparthi");
sb2.setGender("Male");
sb2.setDOB(new Date(System.currentTimeMillis()));
sb2.setEmail("v.p#gmail.com");
sb2.setMobileNumber("8121157248");
sb2.setAddress("Huda");
sb2.setCourses(Arrays.asList("Math"));
PagedResult<StudentBean> pResult = new PagedResult<StudentBean>(
Arrays.asList(sb1, sb2), 2);
pResult.setFirst(0);
pResult.setUnfilteredItems(2);
Integer first = 0;
Integer max = 5;
String sortBy = "firstname";
String sortDir = "asc";
SortablePagedCommand sAndPCommand = new SortablePagedCommand();
sAndPCommand.setFirst(first);
sAndPCommand.setMax(max);
sAndPCommand.setSort(sortBy);
sAndPCommand.setSortDir(sortDir);
Mockito.when(stuClient.getAllStudents(sAndPCommand))
.thenReturn(pResult);
mockMvc.perform(
get("/getHomePage.do").param("first", String.valueOf(first))
.param("max", String.valueOf(max))
.param("sortBy", sortBy)
.param("sortDirection", sortDir))
.andExpect(status().isOk())
.andExpect(view().name("home"))
.andExpect(forwardedUrl("/jsps/home.jsp"))
.andExpect(model().attribute("sortByField", is(sortBy)))
.andExpect(model().attribute("sortDirField", is(sortDir)))
.andExpect(model().attribute("noOfPages", 1))
.andExpect(model().attribute("currentPage", is(first)))
.andExpect(model().attribute("studentList", hasSize(2)))
.andExpect(
model().attribute(
"studentList",
hasItem(allOf(
hasProperty("firstName", is("Sai")),
hasProperty("lastName",
is("Palaparthi")),
hasProperty("gender", is("Male")),
hasProperty(
"dob",
is(new Date(System
.currentTimeMillis()))),
hasProperty("email",
is("v.p#gmail.com")),
hasProperty("mobileNumber",
is("8121157247")),
hasProperty("address", is("Huda")),
hasProperty("courses",
hasItems("Math", "Chem"))))))
.andExpect(
model().attribute(
"studentList",
hasItem(allOf(
hasProperty("firstName",
is("Viswanath")),
hasProperty("lastName",
is("Palaparthi")),
hasProperty("gender", is("Male")),
hasProperty(
"dob",
is(new Date(System
.currentTimeMillis()))),
hasProperty("email",
is("v.p#gmail.com")),
hasProperty("mobileNumber",
is("8121157248")),
hasProperty("address", is("Huda")),
hasProperty("courses", hasItems("Math"))))));
verify(stuClient, times(1)).getAllStudents(sAndPCommand);
verifyNoMoreInteractions(stuClient);
}
}
You're doing it all wrong - this is obviously an integration test so why involve Mockito at all; additionally, you're injecting mocks into the controller field but your tests are not invoking the controller field - they are interacting with the controller bean from your Spring context.
Take a look at this excellent tutorial, especially the unit test part.
To sum it up, your test setup needs to change, you don't need any of the annotations and also you have to use standalone MockMvc setup for your controller:
public class StudentControllerTest {
private MockMvc mockMvc;
#InjectMocks
private StudentController controller;
#Mock
private StudentServiceClient stuClient;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
Mockito.reset(stuClient);
}
Guys the issue is I'm getting the error
Request method 'POST' not supported
Here is my controller :
package com.mintad.spring.controllers;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.mintad.common.beans.Gouvernorate;
import com.mintad.common.service.GouvernorateService;
#RestController
#RequestMapping("/data")
public class DataController {
private static final Logger LOGGER = LogManager.getLogger(DataController.class);
#Autowired
private GouvernorateService gouvernorateService;
#RequestMapping(value = "/gouvernorates", method = RequestMethod.GET, produces = { "application/json" })
public ResponseEntity<List<Gouvernorate>> getGouvernorates() throws FileNotFoundException {
return new ResponseEntity<List<Gouvernorate>>(gouvernorateService.findAll(), HttpStatus.OK);
}
#RequestMapping(value = "/addGouvernorate", method = RequestMethod.POST, produces = { "application/json" })
public #ResponseBody ResponseEntity<Gouvernorate> addGouvernorate(BindingResult result, Model model, #RequestParam(name = "name") String name,
#RequestParam(name = "delegation") String delegation, #RequestParam(name = "district") String district,
#RequestParam(name = "postalCode") String postalCode) throws IOException {
LOGGER.info("addGouvernorate called");
Gouvernorate gouvernorate = new Gouvernorate(name, delegation, district, postalCode);
gouvernorateService.addGouvernorat(gouvernorate);
return new ResponseEntity<Gouvernorate>(gouvernorate, HttpStatus.OK);
}
#RequestMapping(value = "/addTest", method = RequestMethod.POST)
public #ResponseBody String addTest(BindingResult result, Model model) {
LOGGER.info("addGouvernorate called");
Gouvernorate gouvernorate = new Gouvernorate("test", "test", "test", "test");
gouvernorateService.addGouvernorat(gouvernorate);
return "Your Professional Details Updated";
}
}
I've tried so many solutions but in vain.
I'm calling the controller method as follows using chrome Postman application :
http://localhost:8080/mintad/data/addTest (POST)
http://localhost:8080/mintad/data/addGouvernorate?name=test&delegation=test&district=test&postalCode=test (POST too)
I'll be thankful for any help !
The controller version I've added is correct but it works only if I disabled crsf in my security configuration class ass follows :
package com.mintad.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.csrf().disable().
authorizeRequests()
.antMatchers("/", "/home","/data").permitAll()
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
// #formatter:off
}
}
Guys I was completely mistaken in the way I've concieved the controller.
So addTest method is replace by the following :
#RequestMapping(value = "/gouv/", method = RequestMethod.POST)
public ResponseEntity<String> addGouv(#RequestBody Gouvernorate gouv) {
log("addGouv called");
gouvernorateService.addGouvernorate(gouv);
return new ResponseEntity<String>("User created", HttpStatus.CREATED);
}
Where gouvernorateService is autwired.
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");