Testing ControllerAdvice with AutoConfigureMockMvc and CompletableFuture - spring-boot

I have added a REST controller returning CompletableFutures to a project using a ControllerAdvice to translate exceptions into error DTOs.
My controller doesn’t throw the exceptions, wrapping them into failed CompletableFutures and returning these.
When running the full application and manually testing it works as expected, but in my tests the mockMvc won’t trigger the advices and always return HTTP 2xx.
Any idea why?

If you have a standalone setup of MockMvc, then you need to specify the controller advice to be used (if any) while creating the mockMvc instance as follows:
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setControllerAdvice(new YourControllerAdvice())
.build();
The reason for this is that you don't have a context here for spring to detect the controller advice.

I figured out my test was not correct (or, to put it another way.. the testing framework is not designed as I expected ;)
When testing controllers returning CompletableFutures one needs to use asyncDyspatch as in
https://github.com/spring-projects/spring-framework/blob/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java

Related

How can I test a secured endpoint with Awaitility in Spring boot?

I'm using spring boot and I want to assert an asynchronous side effect by calling a secured endpoint with MockMvc.
I have been using Awaitility, but apparently the mocked security context is lost when executing in a different thread.
I couldn't find a way of passing the context, I tried with SecurityContextHolder.setContext() but it didn't work, I guess spring's MockMvc stores the context in a different way.
#Test
#WithMockUser(authorities = "admin", username = "user")
void shouldRunSideEffectAsync() throws Exception {
mockMvc.perform(post("/foo")).andExpect(status().isAccepted());
await()
.atMost(TIMEOUT)
.untilAsserted(() -> mockMvc.perform(get("/foo")).andExpect(status().isOk()));
}
The GET would return 404 for a while and then 200 when the async task is completed. However this will always return 403 as the MockUser info is lost.
How can I solve this?
You almost got it. Security for MockMvc is implemented by TestSecurityContextHolderPostProcessor, which uses the TestSecurityContextHolder to set/get the security context. That is just a wrapper around the SecurityContextHolder.
So you can use TestSecurityContextHolder.setContext() in the awaitility thread and it should work.

How to unit test a class that makes an external WebClient request?

I have an #Service class that does some null checks and then makes a call to an external micro-service using WebClient. Sonar is complaining that this class is not test because the method is not fully tested. Question is, how can I mock this call or use a mockserver for this? I've tried WebTestClient but I can't seem to get things to work..
public Mono<CartResponse> someServiceCall(BarRequest barRequest)
//some null checks and random logic.
return WebClient.create("http://" + fooHostName)
.post()
.uri(uri)
.body(Mono.just(barRequest), BarRequest.class)
.exchange()
.flatMap(serviceResponse -> {
if (serviceResponse.statusCode().is5xxServerError()) {
//some error logic
return Mono.just(barResponse);
}
return serviceResponse.bodyToMono(BarResponse.class);
});
So I don't want to actually make this call I just want it covered in the test, so I'd like some insight on how to get this either mocked or spin up a mock server.. I've been at this for about a day now..
this is as of yet no supported for mocking WebClient like RestTemplate.
there is an open issue for it on github.
Support of MockRestServiceServer for WebClient
Spring themselves use MockWebServer to test their own code so it's safe to say that it is a viable solution.

Is there any way to force Spring to check EL expressions on app start?

I have endpoints in #RestControllers that look similar to this:
#RestController
#RequestMapping("/rest/x")
public class XApiController
{
// ...
#PostMapping(...)
#PreAuthorize("#apiAuthService.canAccessX(#headers)")
public void saveX(...)
{
// ...
}
}
These endpoints require the developer to make the HttpHeaders object available and name it correctly in the method declaration:
public void saveX(#RequestHeader HttpHeaders headers)
Our problem is that if this last step isn't done, the endpoint only fails at runtime when the endpoint is invoked. This means that issues from large refactors later (say, to change the HttpHeaders argument to HttpServletRequest) aren't easy to identify. Is there any way to tell Spring to validate these expressions are valid on app startup?
I suggest you to create integration tests and then invoke saveX from the test to verify this before you deploy an application.
I would also state my opinion that if you want to have testable code with good quality - try to get rid of SpringEL as soon as possible. In my experience this approach proved as poorly testable, hardly maintainable and also introducing unnecessary complications to your source code.
In modern spring framework there are lots of ways to avoid writing SpringEl.
Spring always validates all beans on start up. But your problem is not within validation your problem is test problem. The process of pre authorization is a runtime job. Spring can not know what to do with this expression spring just checks its syntax over SPEL rules.
You can create tests to check header.
You can increase your IDE inspection level of spring spel to error.
You can simply write a static method to get the headers without a rest parameter.

How to test REST in spring app with spring security

I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...

Best way to mock complex soap responses

I have a Java method I want to Unit test, but it requires a mocked SOAP response which contains multiple lists and layers of nodes. I am doing this with a handwritten mock i.e. just manually creating the objects and setting the values, but as the response is quite complex its a pain building up the response. I have a sample XML response is there an easy way of creating the mock using the XML?
Also I looked at Mockito and it looks fine for simple Objects, but it doesnt seem that good for complex responses (I may not be using it to its full potential).
The app stack is Java 1.6, Spring 3 and using JAX-WS.
I do something like this
#WebService
public class MyWebService {
#Autowired
private ServiceBean serviceBean;
public SomeReturedData getData(SomeInputData inputData) {
return serviceBean.getData(inputData);
}
}
For my UnitTest, I have a mock instanciation of "ServiceBean" which I inject in to #MyWebService, and "MyWebService" is deployed using the "in-vm" transport as described here
By Using the in-vm transport, All the XML marshalling/unmarshalling is still done by the web-service framework ,and you only have to deal with Java part.
Now someone might ask, why not test the "ServiceBean" directly, why the need to deply a WS using in-vm transport ? Well 2 things, Using in-vm transport you get to test that the JAXB XML marshalling/unmarshalling is working correctly, and it also allows you to test any intercepting handlers that you might have defined for your webservice.

Resources