I have done a Spring Mvc application and I have been trying to write some integration tests, but I keep getting errors. Here is my AgentService:
#Service
public class AgentService {
#Autowired
private AgentRepository agentRepository;
#Autowired
private AgencyRepository repoAg;
#Autowired
private AgentMapper mapper;
public List<AgentDto> listByAgency(Long id) {
List<Agent> agents = agentRepository.findAll().stream().filter(ag -> ag.getAgency().getId().equals(id)).collect(Collectors.toList());
List<AgentDto> mappedAgents = agents.stream().map(agent -> mapper.mapToDto(agent)).collect(Collectors.toList());
return mappedAgents;
}
}
And my AgentController:
#Controller
public class AgentController {
#Autowired
private AgentService agentService;
#GetMapping("/viewAgentsPage/{id}")
public String viewAgentsPage(#PathVariable(value = "id") Long id, Model model) {
model.addAttribute("listAgents", agentService.listByAgency(id));
return "agents";
}
}
This is the test I've written:
package com.example.awbdproject.controller;
import com.example.awbdproject.models.Agency;
import com.example.awbdproject.models.Agent;
import com.example.awbdproject.services.AgentService;
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.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.ui.Model;
import static org.mockito.Mockito.doReturn;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
#SpringBootTest
#AutoConfigureMockMvc
public class AgentControllerTest {
#Autowired
MockMvc mockMvc;
#MockBean
AgentService agentService;
#MockBean
Model model;
#Test
#WithMockUser
public void showAgentsByAgency() throws Exception {
List<AgentDto> savedAgents = new ArrayList<AgentDto>();
Long id = 1L;
Agency agencyTest = new Agency();
agencyTest.setId(id);
AgentDto agentTest = new AgentDto();
agentTest.setId(1L);
agentTest.setFirstName("test");
agentTest.setLastName("test");
agentTest.setEmail("email#gmail.com");
agentTest.setAgency(agencyTest);
savedAgents.add(agentTest);
when(agentService.listByAgency(id)).thenReturn(savedAgents);
mockMvc.perform(get("/viewAgentsPage/{id}", "1"))
.andExpect(status().isOk())
.andExpect(view().name("agents"))
.andExpect(model().attribute("listAgents", savedAgents))
.andExpect(content().contentType("text/html;charset=UTF-8"));
}
When I run the test I'm getting this error:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /viewAgentsPage/1
Parameters = {}
Headers = []
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest [http://localhost/viewAgentsPage/1]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 302
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Location:"http://localhost/login"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = http://localhost/login
Cookies = []
What seems to be the issue here? I have never used Mockito and can't seem to wrap my head around it. I think it might have something to do with Spring Security. If it helps, this is my security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers(
"/register/**",
"/js/**",
"/css/**",
"/img/**"
).permitAll().anyRequest().authenticated()
.and().formLogin().loginPage("/login")
.permitAll()
.and().logout().invalidateHttpSession(true).clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout").permitAll();
}
Related
This is the Controller as it appears at the moment.
import com.myorg.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class MyController {
private final MyService myService;
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
#GetMapping(value = "/my/homepage")
public String someMethod(ModelMap model, Authentication authentication) {
myService.doSomething(authentication);
return "homepage";
}
}
Here is the Controller Advice to handle exceptions
#ExceptionHandler(HttpStatusCodeException.class)
public ModelAndView handleException(HttpServletRequest req, HttpStatusCodeException ex) {
log.error(ex.getMessage(), ex);
ModelAndView mav = new ModelAndView();
// do stuff
return mav;
}
And this is the current test, which works fine
import com.myorg.service.MyService
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.security.test.context.support.WithMockUser
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
import spock.lang.Specification
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
#SpringBootTest
class MyControllerSpec extends Specification {
#Autowired
private WebApplicationContext webApplicationContext
#SpringBean
private MyService myService = Mock()
#WithMockUser(username = "test#email.com", roles = ["USER"])
def "MyController(test=should not authenticate)"() {
setup:
def notAuthorizedException = new HttpClientErrorException(HttpStatus.UNAUTHORIZED, "blah", null, Charset.defaultCharset())
MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build()
myService.doSomething(_) >> { throw notAuthorizedException }
when:
MvcResult mvcResult = mvc.perform(get("/my/homepage"))
.andExpect(status().is3xxRedirection())
.andReturn()
then:
mvcResult.response.status == 302
mvcResult.modelAndView.viewName == "redirect:/my/login?logout"
}
}
The test passes successfully in that the mock throws the HttpClientErrorException, which the advice catches and then redirects the user to a logout page.
But when I replace the MyService member in the MyController class with a class that implements an interface, the test fails because the HttpClientErrorException is no longer being caught by the advice and instead is being thrown as a nested exception.
Here is the new Controller
import com.myorg.service.MyServiceThatImplementsAnInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class MyController {
private final MyInterface myInterface;
#Autowired
public MyController(MyInterface myInterface) {
this.myInterface = myInterface;
}
#GetMapping(value = "/my/homepage")
public String someMethod(ModelMap model, Authentication authentication) {
myInterface.doSomething(authentication);
return "homepage";
}
}
Here is the interface implementation
#Component("myInterface")
public class MyServiceThatImplementsAnInterfaceImpl implements MyInterface {
private final SomeComponent someComponent;
public MyServiceThatImplementsAnInterfaceImpl(SomeComponent comeComponent) {
this.someComponent = someComponent;
}
#Override
public void doSomething(Authentication authenticationToken) {
someComponent.doSomethingElse();
}
}
And here is the new test
#SpringBootTest
class MyControllerSpec extends Specification {
#Autowired
private WebApplicationContext webApplicationContext
#SpringBean
private MyServiceThatImplementsAnInterfaceImpl myServiceThatImplementsAnInterface = Mock()
#WithMockUser(username = "test#email.com", roles = ["USER"])
def "MyController(test=should not authenticate)"() {
setup:
def notAuthorizedException = new HttpClientErrorException(HttpStatus.UNAUTHORIZED, "blah", null, Charset.defaultCharset())
MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build()
myServiceThatImplementsAnInterface.doSomething(_) >> { throw notAuthorizedException }
when:
MvcResult mvcResult = mvc.perform(get("/my/homepage"))
.andExpect(status().is3xxRedirection())
.andReturn()
then:
mvcResult.response.status == 302
mvcResult.modelAndView.viewName == "redirect:/my/login?logout"
}
}
Here is the failing test stack trace
Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at app//org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at app//org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at app//javax.servlet.http.HttpServlet.service(HttpServlet.java:497)
at app//org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at app//org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
at app//javax.servlet.http.HttpServlet.service(HttpServlet.java:584)
at app//org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at app//org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at app//org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at app//org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
at app//org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at app//org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219)
at app//org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at app//org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
at app//org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at app//org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at app//org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
at app//org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at app//org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at app//org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at app//org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at app//org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at app//org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurer$DelegateFilter.doFilter(SecurityMockMvcConfigurer.java:132)
at app//org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at app//org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:201)
at com.myorg.controller.MyControllerSpec.<where my test is>
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at com.myorg.controller.MyControllerSpec.<where my test is>
Any help is appreciated
Problem solved and, as with about 90% of these kinds of problems, the error was simple to fix. In my test class, I simply hadn't assigned the exception handler class to the MockMvc.
def "MyController(test=should not authenticate)"() {
setup:
Authentication authentication = //Just mock out an Authentication object here
def notAuthorizedException = //As before, just mock out an exception
myInterface.doSomething(_) >> { throw notAuthorizedException }
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver()
viewResolver.setPrefix("prefix")
MyController myController = new MyController(myServiceThatImplementsAnInterface)
MockMvc mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.setControllerAdvice(new MyExceptionHandler())
.setViewResolvers(viewResolver)
.build()
when:
MvcResult mvcResult = mockMvc.perform(get("/my/homepage")
.principal(authentication))
.andExpect(status().is3xxRedirection())
.andReturn()
then:
mvcResult.response.status == 302
mvcResult.modelAndView.viewName == "redirect:/app/login?logout"
}
#ControllerAdvice(assignableTypes = MyInterface.class)
public class MyExceptionHandler {
#ExceptionHandler(HttpStatusCodeException.class)
public ModelAndView handleException(HttpServletRequest req, HttpStatusCodeException ex) {
log.error(ex.getMessage(), ex);
ModelAndView mav = new ModelAndView();
// do stuff
return mav;
}
}
So in my Spring controller, i have this route
#RequestMapping(value = "myroute", method = POST)
public String myRoute(HttpServletRequest) {
...
}
We use MockMvcBuilder to mock the POST calls.
MockHttpServletRequestBuilder post = post(myRouteUrl);
mockMvc.perform(post).andExpect(status().isOK());
Now if i just change post to get, the status back is still 200.
So how to verify GET method is failing ?
Using Java 1.8.
Thanks !
I got a HTTP error 405 when I tried the following. Is there something else missing from the example shown?
This is the controller class I used.
package demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
#RestController
public class MyController {
#RequestMapping(value = "/myroute", method = GET)
public String myRoute(HttpServletRequest request) {
System.out.println("HERE");
return "good";
}
}
This is the test class I used.
package demo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ExtendWith(MockitoExtension.class)
#WebMvcTest(MyController.class)
class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void myRoute() throws Exception {
MockHttpServletRequestBuilder post = post("/myroute");
mockMvc.perform(post).andExpect(status().isOk());
}
}
Error Message:
Error message = Request method 'POST' not supported
Headers = [Allow:"GET"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Status expected:<200> but was:<405>
Expected :200
Actual :405
I want to be able to set a context variable from the http request header I receive from the request. This will be a jwt token so I can identify my user on every query.
package br.com.b2breservas.api;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URL;
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
#Component
public class GraphQLProvider {
#Autowired
GraphQLDataFetchers graphQLDataFetchers;
private GraphQL graphQL;
#PostConstruct
public void init() throws IOException {
URL url = Resources.getResource("schema.graphqls");
String sdl = Resources.toString(url, Charsets.UTF_8);
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("books", graphQLDataFetchers.getBooks()))
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}
#Bean
public GraphQL graphQL() {
return graphQL;
}
}
You can create a customized object which internally contain the JWT or simply the HttpServletRequest :
public class GraphQLContext {
private HttpServletRequest httpServletRequest;
}
When executing a GraphQL query , you create this context object and set it to the ExecutionInput. Most web framework should provide some ways to access the current HttpServletRequest easily :
GraphQLContext context = new GraphQLContext(httpServletRequest);
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.context(context)
.build();
ExecutionResult result = graphQL.execute(executionInput);
Then in the data fetcher , the context can be get by :
#Override
public Object get(DataFetchingEnvironment env) throws Exception {
GraphQLContext context = env.getContext();
HttpServletRequest httpServletRequest = context.getHttpServletRequest();
}
You can inject (or Autowire) your custom GraphQLInvocation instance which can act as an interceptor for all requests handled by GraphQL
import graphql.ExecutionInput
import graphql.ExecutionResult
import graphql.GraphQL
import graphql.spring.web.servlet.GraphQLInvocation
import graphql.spring.web.servlet.GraphQLInvocationData
import org.springframework.context.annotation.Primary
import org.springframework.stereotype.Component
import org.springframework.web.context.request.WebRequest
import java.util.concurrent.CompletableFuture
#Component
#Primary // <= Mark it as Primary to override the default one
class ErsanGraphQLInvocation(private val graphQL: GraphQL) : GraphQLInvocation {
override fun invoke(invocationData: GraphQLInvocationData,
webRequest: WebRequest): CompletableFuture<ExecutionResult> {
val context = "Context" //Basically any class you want <=====
val executionInput = ExecutionInput.newExecutionInput()
.query(invocationData.query)
.operationName(invocationData.operationName)
.variables(invocationData.variables)
.context(context)
.build()
return graphQL.executeAsync(executionInput)
}
}
and then in your DataFetcher, you can read the context from the DataFetchingEnvironment instance, eg.
fun appVersionFetcher(): DataFetcher<Boolean> {
return DataFetcher { dataFetchingEnvironment ->
val context = dataFetchingEnvironment.getContext<String>()
println("Context $context")
false
}
}
Background: Search form in a webapp, with an auto-complete/suggestion. Using jQuery's autocomplete, suggestions are shown after typing a few characters. These suggesitons are retrieved as JSON data from one of the webapp's controllers.
Issue: I am testing the application via the HtmlUnit Integration of Spring Test, which works fine for Text/Html Pages, but for the JSON responses here, the setup seems to fail (see error below).
Manually testing (the actual webapp) via Browser works and fetching JSON from "real" pages works as well ( see "json()" test) -> Should testing JSON responses work via HtmlUnit / Spring Test setup and if yes, what am I doing wrong?
Update (2017-06-21):
Using
#ResponseBody String
and building the JSON "manually" (not letting Spring automagically doing it) works; not really what I wanted, but at least I can properly test it this way ...
Test:
package my.project;
import com.gargoylesoftware.htmlunit.*;
import org.junit.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.*;
import org.springframework.test.*;
import org.springframework.web.context.support.GenericWebApplicationContext;
import javax.servlet.ServletException;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestWebAppConfig.class,
// avoid context caching
MyControllerIT.class})
#WebAppConfiguration
public class MyControllerIT {
protected static final String SERVER_URL = "http://localhost";
protected WebClient webClient;
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
#Autowired
protected GenericWebApplicationContext webApplicationContext;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.request.setServerName("Gondor.Osgiliath");
this.response = new MockHttpServletResponse();
this.webClient = this.initClient();
}
protected final WebClient initClient() throws ServletException {
DefaultMockMvcBuilder mockMvcBuilder = MockMvcBuilders.webAppContextSetup(this.webApplicationContext);
MockMvc mockMvc = mockMvcBuilder.build();
WebClient webClient = new WebClient();
webClient.setWebConnection(new MockMvcWebConnection(mockMvc));
return webClient;
}
// https://stackoverflow.com/questions/2932857/html-handling-a-json-response
#Test
public void json() throws Exception {
webClient = new WebClient(BrowserVersion.INTERNET_EXPLORER, "some.proxy", 7890);
Page page = webClient.getPage("https://stackoverflow.com/users/flair/97901.json");
WebResponse webResponse = page.getWebResponse();
String contentType = webResponse.getContentType();
String contentAsString = webResponse.getContentAsString();
}
#Test
public void suggestShouldReturnJSON() throws Exception {
Page page = webClient.getPage(SERVER_URL + MyController.SUGGEST_URL + "?term=asdf");
WebResponse webResponse = page.getWebResponse();
String contentType = webResponse.getContentType();
String contentAsString = webResponse.getContentAsString();
}
#Test
public void suggestShouldReturnJSONViaMockMvc() throws Exception {
MockMvc springMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
ResultActions resultActions = springMvc.perform(MockMvcRequestBuilders
.get(MyController.SUGGEST_URL + "?term=asdf")
.accept(MediaType.APPLICATION_JSON_VALUE));
resultActions.andDo(MockMvcResultHandlers.print());
}
}
Controller:
package my.project;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Arrays;
import java.util.List;
#Controller
public class MyController {
public static final String SUGGEST_URL = "/suggest";
//http://api.jqueryui.com/autocomplete/#option-source
#RequestMapping(value = {SUGGEST_URL,}, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
List<Suggestion> suggest(#RequestParam(name = "term") String term) {
return Arrays.asList(new Suggestion("label1", "value1"), new Suggestion("label2", "value2"));
}
}
Config (Thymeleaf omitted for the moment):
package my.project;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#EnableWebMvc
#ComponentScan(basePackages = {"my.project.*",})
#Configuration
#Import(ThymeleafConfig.class)
abstract class TestWebAppConfig extends WebMvcConfigurerAdapter {
}
Error:
com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException: 406 Not Acceptable for http://localhost/suggest?term=asdf
at com.gargoylesoftware.htmlunit.WebClient.throwFailingHttpStatusCodeExceptionIfNecessary(WebClient.java:571)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:396)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:304)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:451)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:436)
at my.project.MyControllerIt.suggestShouldReturnJSON(MyControllerIt.java:
...
I want to test my controller with the help of JUnit. I am new to this. I have written some code for this but it is not coming to my function listCard. My controller is:
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping("/v1/card")
#Configuration
public class CardManagementController {
private final static Logger LOG = LoggerFactory
.getLogger(CardManagementController.class);
#Autowired
ICardService cardService;
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.GET)
public #ResponseBody GetCardResponse getCard(
#ModelAttribute #Valid GetCardRequest request, BindingResult results)
throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.error("error occured occur while fetching card response");
throw new ValidationException(
"Error Occoured while validiating card request");
}
GetCardResponse response = null;
response = cardService.getCard(request);
return response;
}
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.POST)
public #ResponseBody AddCardResponse addCard(
#ModelAttribute AddCardRequest request, BindingResult results)
throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.error("error occured while adding the card");
throw new ValidationException(
"Error Occoured while validiating addcard request");
}
LOG.debug("add Card with POST method");
AddCardResponse response = null;
response = cardService.addCard(request);
return response;
}
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.DELETE)
public #ResponseBody DeleteCardResponse deleteCard(
#ModelAttribute #Valid DeleteCardRequest request,
BindingResult results) throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.debug("error occured while delting the card");
throw new ValidationException(
"Error Occoured while validiating delete card request");
}
DeleteCardResponse response = null;
response = cardService.deleteCard(request);
return response;
}
#RequestMapping(value = RestURIConstants.LISTCARD, produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.GET)
public #ResponseBody ListCardResponse listCard(
#ModelAttribute #Valid ListCardRequest request) throws RuntimeException, ValidationException {
ListCardResponse response = null;
response = cardService.listCards(request);
return response;
}
#ExceptionHandler({ ValidationException.class})
#ResponseBody
public CPPException handleValidationException(ValidationException ex) {
LOG.error("Exception occoured",ex);
CPPException exception = new CPPException(ex.getMessage());
exception.setStatus(500);
return exception;
}
#ExceptionHandler({RuntimeException.class})
#ResponseBody
public CPPException handleException(RuntimeException ex) {
LOG.error("Exception occoured", ex);
CPPException exception = new CPPException("Internal Server Error");
exception.setStatus(500);
return exception;
}
}
and I have written the following code for testing:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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.setup.MockMvcBuilders;
import com.my.cpp.controller.CardManagementController;
import com.my.cpp.request.ListCardRequest;
import com.my.cpp.service.impl.CardServiceImpl;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:/spring/application-context.xml"})
public class CardApiTest {
private MockMvc mockMvc;
//#Autowired
private CardManagementController cm=new CardManagementController();
#Autowired
private CardServiceImpl cmr;
#Before
public void setUp() throws Exception {
mockMvc= MockMvcBuilders.standaloneSetup(cm).build();
}
#Test
public void testList() throws Exception{
final ListCardRequest lr=new ListCardRequest();
this.mockMvc.perform(get("/v1/card/list?"));
}
}
First - Remove the #Configuration annotation from your controller. It doesn't belong here.
Second - Consider using Mockito while testing, since you have a service injected in your controller. Your updated test class should look something similar as below
#RunWith(MockitoJUnitRunner.class)
public class CardApiTest {
private MockMvc mockMvc;
#InjectMocks
private CardManagementController cm;
#Mock
private ICardService cardService;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(cm).build();
// Instantiate cardListRequest and cardListResponse here
when(cardService.listCards(cardListRequest)).thenReturn(cardListResponse);
}
#Test
public void testList() throws Exception{
this.mockMvc.perform(get("/v1/card/list?"));
}
}
Let know in comments if you need further info / assistance.