I am trying to test my application with junit.
Therefore I've setup the following class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/META-INF/spring/applicationContext-test.xml" )
#TransactionConfiguration
#Transactional
public class DispatcherServletTest extends AbstractJUnit4SpringContextTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private DispatcherServlet dispatcher;
#Before
public void setUp() throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
MockServletConfig config = new MockServletConfig("myapp");
config.addInitParameter("contextConfigLocation","classpath*:webmvc-config.xml");
dispatcher = new DispatcherServlet();
dispatcher.init(config);
}
//test cases
}
So the problem is, that it seems that my dispatcher servlet cannot send any request to any of my controllers.
I think that there is something with the configuration - contextConfigurationLocation.
It looks like he can find the file (otherwise it would throw an exception) , but doesn't load any configuration
The logger says:
org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [http://localhost:8080/myapp/abc]
But I have absolutely no idea what's wrong...
I would appreciate any help!
Thanks in advance
Mines are working fine, try the following tweaks.
if you're using Junit4 no need to extend you test class, the junit runner should do the trick
Load the context config via classpath, and make sure is accessible from the test classpath
#ContextConfiguration(locations={"classpath:applicationContext-test.xml"})
then just test the annotated controllers. I do it like this:
#Test
#Transactional
public void testAnnotatedListUser() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
AnnotationMethodHandlerAdapter handlerAdpt = new AnnotationMethodHandlerAdapter();
request.setRequestURI("/you/URIhere");
ModelAndView mav = handlerAdpt.handle(request, response, this.controller);
assertEquals("Incorrect view name returned", "myexpectedviewname", mav.getViewName());
}
There are several Problems in my question:
At first, it is not possible to extend the AbstractJUnit4SpringContextTests and use #RunWith(...), because it's the same.
At second, you should not use the dispatcherServlert, but an Handler by defining the handler in you application.xml and autowiring it in the test case via #Autowire private Handler handler...
Then everything should work fine!
Related
I am trying to test the security configuration of some of my endpoints which are secured with #PreAuthorize(#oauth2.hasScope('scope'). When accessing such an endpoint via Postman with a access token that does not have the required scope, the following is returned with HTTP status code 403 (forbidden):
{
"error": "insufficient_scope",
"error_description": "Insufficient scope for this resource",
"scope": "scope"
}
Which is the expected behaviour that I want.
When trying to test this configuration, Springs NestedServletException interferes with my test case before it can complete with my expected result.
This is a simplified version of the controller I want to test:
#RestController
#RequestMapping(value = "/api")
public class OauthTestingResource {
#PreAuthorize(#oauth2.hasScope('scope'))
#RequestMapping(value = "/scope", method = RequestMethod.GET)
public void endpoint() {
// ...
}
}
And this is the corresponding test case:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
#WebAppConfiguration
public class AuthorizationTest {
#Autowired
protected WebApplicationContext webApplicationContext;
protected SecurityContext securityContext = Mockito.mock(SecurityContext.class);
#Before
public void setup() throws Exception {
this.mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
SecurityContextHolder.setContext(securityContext);
}
protected Authentication createMockAuth(Client client) {
final List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
final Authentication pwAuth = new UsernamePasswordAuthenticationToken("testuser", "testpw", authorities);
final TokenRequest request = new TokenRequest(new HashMap<>(), client.getClientId(), client.getScopes(), "password");
final OAuthClient oauthClient = new OAuthClient(client, GrantType.PASSWORD);
return new OAuth2Authentication(request.createOAuth2Request(oauthClient), pwAuth);
}
#Test
public void testAppScope() throws Exception {
final Client client = new Client("id1", "secret1");
client.setScope("scope");
Mockito.when(securityContext.getAuthentication()).thenReturn(createMockAuth(client));
// this test passes
mvc.perform(get("/api/scope")).andExpect(status().isOk());
client.setScope("other_scope");
Mockito.when(securityContext.getAuthentication()).thenReturn(createMockAuth(client));
// NestedServletException thrown here
mvc.perform(get("/api/scope")).andExpect(status().isForbidden());
}
}
The exception that is thrown is the following (which is expected):
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.springframework.security.access.AccessDeniedException:
Insufficient scope for this resource
My question is how can I prevent this exception from interfering with my test case?
I did spring security test cases by following this link. Things worked fine except this issue of nesting original exception in NestedServletException.
I did not find any direct way to figure this out but AspectJ helped me in handling this in a cleaner way.
We can use the static assertThatThrownBy() method of the Assertions class. This method returns an AbstractThrowableAssert object that we can use to write assertions for the thrown exception.
The code that captures an exception thrown by the methodThatThrowsException() method looks as follows:
assertThatThrownBy(() -> methodThatThrowsException())
.isExactlyInstanceOf(DuplicateEmailException.class);
Thanks to this excellent blog where you can find additional details.
The way in which I handled this in my test case would be (by taking your test case codeline):
org.assertj.core.api.Assertions.assertThatThrownBy(() -> mvc.perform(get("/api/scope")).andExpect(status().isOk())).hasCause(new AccessDeniedException("Access is denied"));
That way your test case would be able to assert actual AccessDeniedException that is nested in NestedServletException.
I had a similar case and suppressed the NestedServletException by using
#Test(expected = NestedServletException.class) and then I was able to get hold of the MvcResult and do further asserts on it as in other tests like:
// then
MvcResult result = resultActions.andExpect(status().isServiceUnavailable()).andReturn();
String message = result.getResponse().getContentAsString();
assertThat(message).contains("ABC");
assertThat(result.getResolvedException().getClass()).isEqualTo(XYZ.class);
It seemed to work.
I fixed it by adding an #ExceptionHandler for that exception. Seems like if MockMvc throws an actual exception it means you don't "handle" this case which is not ideal.
If you are using a centralised class to do your error handling via AOP (using #ControllerAdvice annotation), then include this ControllerAdvice in your test setup.
#BeforeEach
public void setup() {
this.mockMvc = standaloneSetup(myController).setControllerAdvice(MyErrorHandlingAdvice.class).build();
}
I'm using Spring 3.2.11.RELEASE and JUnit 4.11. In a particular Spring controller, I have a method that ends thusly ...
return new ModelAndView(new RedirectView(redirectUri, true));
In my JUnit test, how do I verify return from a submission to my controller in which this RedirectView is returned? I used to use org.springframework.test.web.AbstractModelAndViewTests.assertViewName, but that only returns "null", even when a non-empty ModelAndView object is returned. Here is how I'm constructing my JUnit test ...
request.setRequestURI(“/mypage/launch");
request.setMethod("POST");
…
final Object handler = handlerMapping.getHandler(request).getHandler();
final ModelAndView mav = handlerAdapter.handle(request, response, handler);
assertViewName(mav, "redirect:/landing");
Any help on how to verify that a RedirectView comes back with the proper value is appreciatd,
As Koiter said, consider moving to spring-test a and MockMvc
It providers some methods to test controllers and requests/reponses in a declarative way
you will need a #Autowired WebApplicationContext wac;
and on your #Before method setup this to use the #WebAppConfiguration of the class.
You'll end up with something
#ContextConfiguration("youconfighere.xml")
//or (classes = {YourClassConfig.class}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class MyControllerTests {
#Autowired WebApplicationContext wac
private MockMvc mockMvc;
#Before
public void setup() {
//setup the mock to use the web context
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}
Then you just need to use the MockMvcResultMatchers to assert things
#Test
public void testMyRedirect() throws Exception {
mockMvc.perform(post("you/url/")
.andExpect(status().isOk())
.andExpect(redirectUrl("you/redirect")
}
Note: post(), status() isOk() redirectUrl() are statics imports from MockMvcResultMatchers
See more what you can match here
Considering change your tool to MockMvc.
First you should create your MockMvc based on your controller.
private MockMvc mockController;
mockController =
MockMvcBuilders.standaloneSetup(loginController).setCustomArgumentResolvers(
new ServletWebArgumentResolverAdapter(new PageableArgumentResolver())).build();
After you create that object build the request with the request information. Part of this is the assert options that are contained in the API.
mockController.perform(MockMvcRequestBuilders.get(LoginControllerTest.LOGIN_CONTROLLER_URL + "?logout=true").
principal(SessionProvider.getPrincipal("GonLu004")))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("jsp/login"))
.andExpect(MockMvcResultMatchers.model().attribute("logOutMessage", logoutMessage));
The MockMvcResultMatchers contains a method for reviewing redirect information.
MockMvc from spring is a good choice to apply your unit testing on the controller layer.
I am trying to test a Spring Boot RestController that should use custom error attributes.
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(
RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Throwable error = getError(requestAttributes);
return errorAttributes;
}
};
}
But when i try to test the custom error attributes using a simple test these properties are not taken into account. The test below actually fires a request and i except that the custom attributes are used. But whatever i seem to do the code seems to be not taken into account.
class TestSpec extends Specification {
MockMvc mockMvc
def setup() {
mockMvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build()
}
def "Test simple action"() {
when:
def response = mockMvc.perform(post("/hello")
.contentType(MediaType.APPLICATION_JSON)
.content('{"sayHelloTo": ""}')
)
then:
response.andExpect(status().isOk())
}
}
Any clue on how i could test if the custom attributes?
Spring Boot's error infrastructure works by forwarding requests to an error controller. It's this error controller that uses an ErrorAttributes instance. MockMvc only had fairly basic support for testing the forwarding of requests (you can check that the request would be forwarded, but not the actual outcome of that forward). This means that a MockMvc test that calls your HellowWorldController, either using standalone setup or a web application context-based setup, isn't going to drive the right code path.
A few options:
Unit test your custom ErrorAttributes class directly
Write a MockMvc-based test that calls Spring Boot's BasicErrorController configured with your custom ErrorAttributes instance
Write an integration test that uses RestTemplate to make an actual HTTP call into your service
The test class from Spring gives you a good place to start your own tests!
Create an instance of your custom error attributes class and use MockHttpServletRequest and WebRequest:
private final DefaultErrorAttributes errorAttributes = new YourCustomErrorAttributes();
private final MockHttpServletRequest request = new MockHttpServletRequest();
private final WebRequest webRequest = new ServletWebRequest(this.request);
For your test method:
//Set the appropriate error state in your mocked request object:
RuntimeException ex = new RuntimeException("Test");
this.request.setAttribute("javax.servlet.error.exception", ex);
//Pass the mocked request into the the methods that are normally called by the framework
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, ErrorAttributeOptions.of(Include.STACK_TRACE));
// add your own asserts
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
I would like to be able to test a route which consumes from a queue then does some work in a bean involving a spring injected service and use mockito to effectively mock out this service.
My spring route is as follows:
<camel:route id="msgemailqueue-to-emailservice">
<camel:from uri="activemq:emails" />
<camel:bean ref="emailService" method="createEmailRequest"/>
</camel:route>
The emailService bean has an autowired service which is then called in the createEmailRequest() which goes off to another service and retrieves user data to be used subsequently.
The test:
#RunWith(MockitoJUnitRunner.class)
public class TroubledEmailServiceImplTest extends CamelSpringTestSupport {
#Produce(context = "messagingCamelContext")
protected ProducerTemplate producer;
#Mock
private UserRestService userRestService;
#Override
protected AbstractApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("messaging-camel-route-test-context.xml");
}
#Test
public void testUserResponseToEmailQueue() throws Exception {
context.addRoutes(new MyDynamcRouteBuilder(context, "direct:addEmailRequest", "activemq:emails"));
Mockito.when(userRestService.getUserById(Mockito.anyLong())).thenReturn(
new WebServiceResult<UserVO>(new UserVO()));
CreateMessageRequest msgReq = new CreateMessageRequest();
producer.sendBody("direct:addEmailRequest", msgReq);
Mockito.verify(userRestService).getUserById(Mockito.anyLong());
assertMockEndpointsSatisfied();
}
The bean as follows:
#Override
public void createEmailRequest(final CreateMessageRequest request) throws CreateEmailException {
LOGGER.trace("Entering createEmailRequest(request) " + Arrays.asList(new Object[] { request }));
Validate.notNull(request, "CreateMessageRequest was null");
WebServiceResult<UserVO> response;
try {
response = userRestService.getUserById(request.getId());
} catch (final WebServiceException e) {
throw new CreateEmailException("Error lookup up user data for email", e);
}
final UserVO userResponse = response.getData();
All compiles ok and when running the route fires as an object is popped on the queue which is then passed to the bean and the createEmailRequest is invoked and the call to the mockito mocked service happens ok
response = userRestService.getUserById(request.getId());
but the response is null even though
Mockito.when(userRestService.getUserById(Mockito.anyLong())).thenReturn(
new WebServiceResult<UserVO>(new UserVO()));
was performed in the test. It appears that the service in bean is a different instance i.e. mockito mock is never invoked.
I am doing something wrong and perhaps my testing approach is all wrong as well but should this work in theory? I'd really like to be able to mock out a service in a bean in my camel route.
I'm using Camel Enhanced Spring Test and have passed through the same issue. I only changed #Mock to #MockBean. My Camel version is 2.18.
the mock userRestService you create in the test has to be the same instance you use in the bean. I do not see where you are setting the userRestService for the createEmailRequest method. That service needs to be the same mock object as you create in your test.
I have resolved this - mea culpa. My test class was effectively creating two instances of the service - one through the spring application context and another due to the #RunWith(MockitoJUnitRunner.class) plus #mock annotation. Now resolved by doing the mock creation once. To sum up this was a spring wiring issue only on my part. Many thanks #mike-pone.
As part of Spring 3 MVC it is possible to inject the currently logged in user (Principle) object into a controller method.
E.g.
#Controller
public class MyController {
#RequestMapping(value="/update", method = RequestMethod.POST)
public String update(ModelMap model, Principal principal) {
String name = principal.getName();
... the rest here
}
}
This is documented as part of the Spring 3 documentation here:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments.
This works in the production code. However I don't know how to test this.
When I create an integration test (having set up spring security context as well)
and call the controller handle method then the Principal is always null!
public class FareTypeControllerIntegrationTest extends SpringTestBase {
#Autowired
private MyController controller;
#Autowired
private AnnotationMethodHandlerAdapter handlerAdapter;
private final MockHttpServletRequest request = new MockHttpServletRequest();
private final MockHttpServletResponse response = new MockHttpServletResponse();
#Test
public void testUpdate() throws Exception {
request.setRequestURI("/update");
request.setMethod(HttpMethod.POST.name());
... setup rest of request
ModelAndView mav = handlerAdapter.handle(request, response, controller);
.. rest of assertions
}
The tests are running correctly and everything except the Principal is null.
Any ideas?
TIA
Ayub
After a quick look into Spring sources this should work:
request.setUserPrincipal(somePrincipal);
I've tried to do this some time ago, here is the method i used to set up authentication.
protected void setSecurityContext(String login){
userDetailsTest = userManager.loadUserByUsername(login);
TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(userDetailsTest, userDetailsTest.getAuthorities());
SecurityContext securityContext = new SecurityContextImpl();
securityContext.setAuthentication((Authentication) testingAuthenticationToken);
SecurityContextHolder.setContext(securityContext);
}
Then i just call it in the #Before method of the test.
Hope it helps.
I do something like this in my tests prior to calling code using Spring Security (such as the Principal parameter resolver you are testing):
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("wiseau", "Love is blind"));