Looking for Simple Way To Unit Test Spring-WS Endpoint Methods - spring

As the topic states, I am looking for a simple was to test ONLY the endpoint mapping urls for a Spring-WS service. For example, here is a sample method:
#Endpoint
public class ServiceClass {
private static final String NAMESPACE_URI = "http://mysite.com/thisApp/schemas";
#PayloadRood(namespace = NAMESPACE_URI, localPart="Method1")
#ResponsePayload
public ResponseObject method1(#RequestPayload RequestObject request) {
// do something
// return the ResponseObject
}
}
All I want to do is verify that a call to http://mysite.com/thisApp/schemas{serviceName actually hits a method; I don't care (yet) whether there is a response. In other words, I need to ensure that a NoEndpointFoundException is not returned.
I know Spring-WS has mock servers and clients, but I'm not quite seeing what I'm looking for.
Thanks for any help.

You can see if rest-assured maybe helpful for your unit test.

Related

Cannot understand mocks

I'm trying to write tests for my Spring Boot application that has some end points, it's a REST application.
I have the "usual" simple web application with a controller, a service and a repository. CRUD operations.
In my update endpoint I call the service layer to perform the update, like this:
#PutMapping
public Post updatePost(#RequestBody Post post) {
return postService.updatePost(post);
}
The updatePost method on the PostService class makes some checks about the object before updating in it, and if the checks all pass, then the update operation is perforrmed, like this:
public Post updatePost(Post post) {
if (post == null || post.getId() == null) {
throw new PostGenericException();
}
Post postToUpdate = postRepo.findById(post.getId()).orElseThrow(PostGenericException::new);
bool isOk = true;
// some other checks..
if (!isOk) {
throw new PostGenericException();
}
// update operation
postToUpdate.setMessage(post.getMessage());
....
return postRepo.save(postToUpdate);
}
From what I've seen online in the test class I have to do something like this:
#WebMvcTest(PostController.class)
public class PostControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private PostService postService;
#Test
public void updatePost() throws Exception {
Post post = new Post(...);
Mockito.when(postService.updatePost(post)).thenReturn(post);
mockMvc.perform(MockMvcRequestBuilders.put("/posts")
.contentType(MediaType.APPLICATION_JSON)
.content(this.mapper.writeValueAsString(post)))
.andExpect(status().isOk())
.andExpect(jsonPath("$", notNullValue()));
}
}
So here in the test method I'm completely replacing the logic of the updatePost method of the service layer with a fake one.
How is this useful?
The only good reason I can think of is that here I'm trying to test the endpoint by itself, meaning that I simply want to check if I can reach that endpoint, but I don't really care about the implementation at all, i.e how the update operation is performed. I'm expecting that if I make a PUT request to that endpoint I get a result, if the test fails I know that the controller doesn't handler that endpoint anymore.
Is this all about it or am I missing something?
If I remember correctly, Kent Beck also said that you don't want to test the implementation but only the public APIs, it doesn't make much sense to test the implementation anyway, you could have a lot of tests and at some point have more test code than production code.
Using mocks may be against your testing philosophy, but looking at the practicalities:
Web layer is a nice chunk to be tested separately. There is a good amount of things that you can check:
routing
request and response deserialization
validation
error handling
authentication
This also allows business logic tests to skip these concerns.
Additional benefits:
they are easy to set up and run on a single machine (or even single process)
they are reasonably fast

Testing functions in spring boot which have autowired dependencies

Here is a minimal version of code that I want to test:
#Service
public class Ver {
#Autowired
private DHClient dhclient;
#Autowired
private DHUrlService dhUrlService;
public String getLVersion(String LRversion)
{
String vers=dhclient.get(dhUrlService.getUrl());
return getVersion(vers, LRversion);
}
}
Now the problem with testing this is that. dhclient is autowired inside the Ver class.
The get function makes an HTTP Get request and fetches a response.
I want the test case to actually make the dhclient.get call and return the result.
Any ideas on how to go about testing this?
How do I test that getLVer(String lrVersion) returns null when "test" is passed as the argument?
#Service
public class DHClient {
#Value("${clientId}")
private String clientId;
private HttpClient httpClient;
public String get(String URL){
HttpRequest request// build the HTTP request
HttpResponse<String> response = httpCient.send(request,HttpResponse.BodyHandlers.ofString());
return response.body()
}
}
the above is the code of the DHClient. Since I want to make an actual GET call using the get function. Probably mocking the object is not a good idea. The alternative would be to create an actual object. But this has fields like clientId and httpClient which are initialized by Spring. I am not sure how to create an actual object for this.

How to initialize Jackson on Spring Boot start to have fast 1st request?

Problem
I have a simple Spring Boot app with a basic RestController (full code available here). It consumes JSON and uses Jackson to convert request from JSON and response to JSON.
#RestController("/")
#RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public class SomeController {
#Autowired
private SomeService someService;
#PostMapping
public ResponseEntity<SomeResponseDto> post(#RequestBody #Valid SomeRequestDto someRequestDto) {
final SomeResponseDto responseDto = new SomeResponseDto();
responseDto.setMessage(someRequestDto.getInputMessage());
responseDto.setUuid(someService.getUuid());
return ResponseEntity.ok(responseDto);
}
After start-up, the 1st request is about 10-times slower than any sub-sequent request. I debugged and profiled the app and it seems that on first request a Jackson JSON parser is getting initialized somewhere in AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters and AbstractJackson2HttpMessageConverter.
In sub-sequent requests, it seems to get re-used.
Question
How do I initialize Jackson JSON parsing during start-up so that also 1st request is fast?
I know how to trigger a method after Spring started. In PreloadComponent I added as an example how to do a REST request against the controller.
#Component
public class PreloadComponent implements ApplicationListener<ApplicationReadyEvent> {
private final Logger logger = LoggerFactory.getLogger(PreloadComponent.class);
#Autowired
private Environment environment;
#Autowired
private WebClient.Builder webClientBuilder;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// uncomment following line to directly send a REST request on app start-up
// sendRestRequest();
}
private void sendRestRequest() {
final String serverPort = environment.getProperty("local.server.port");
final String baseUrl = "http://localhost:" + serverPort;
final String warmUpEndpoint = baseUrl + "/warmup";
logger.info("Sending REST request to force initialization of Jackson...");
final SomeResponseDto response = webClientBuilder.build().post()
.uri(warmUpEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.body(Mono.just(createSampleMessage()), SomeRequestDto.class)
.retrieve()
.bodyToMono(SomeResponseDto.class)
.timeout(Duration.ofSeconds(5))
.block();
logger.info("...done, response received: " + response.toString());
}
private SomeRequestDto createSampleMessage() {
final SomeRequestDto someRequestDto = new SomeRequestDto();
someRequestDto.setInputMessage("our input message");
return someRequestDto;
}
}
This only works in this toy example. In reality, I have many REST endpoints with complex DTOs and I would need to add a "warm-up" endpoint next to each "real" endpoint as I can't call my real endpoints.
What I already tried?
I added a second endpoint with a different DTO and called it in my PreloadComponent. This doesn't solve the problem. I assume that an Jackson / whatever instance is created for each type.
I autowired ObjectMapper into my PreloadComponent and parsed JSON to my DTO. Again, this doesn't solve the issue.
Full source available at: https://github.com/steinsag/warm-me-up
It turns out that Jackson validation is the problem. I added the JVM option
-verbose:class
to see when classes get loaded. I noticed that on 1st request, there are many Jackson validation classes getting loaded.
To confirm my assumption, I re-worked my example and added another independent warm-up controller with a distinct DTO.
This DTO uses all Java validation annotations also present like in the real DTO, e.g. #NotNull, #Min, etc. In addition, it also has a custom enum to also have validation of sub-types.
During start-up, I now do a REST request to this warm-up endpoint, which doesn't need to contain any business logic.
After start-up, my 1st request is now only 2-3 times slower than any sub-sequent requests. This is is acceptable. Before, the 1st request was 20-40 times slower.
I also evaluated if really a REST request is needed or if it is sufficient to just do JSON parsing or validation of a DTO (see PreloadComponent). This reduces runtime of 1st request a bit, but it is still 5-15 times slower than with proper warm-up. So I guess a REST request is needed to also load other classes in Spring Dispatcher, etc.
I updated my example at: https://github.com/steinsag/warm-me-up
I believe, that a lot of classes will be lazy-loaded. If first call performance is important, then I think warming up by calling each endpoint is the way to go.
Why do you say, that you cannot call the endpoints? If you have a database and you don't want to change the data, wrap everything in a transaction and roll it back after the warm up calls.
I haven't seen any other method to solve this, which doesn't necessarily mean, that it doesn't exist ;)

#Gateway is not required if we use #MessagingGateway

From Spring 4.0 onwards #MessagingGateway was introdued. Using it if we have only one gateway method in our Gateway interface , then we don't need to annotate the Gateway method with #Gateway.
Below is my example, where both are working.
So, my question is can we stop using #Gateway when we have only one method in Gateway interface?
Code-1:
#MessagingGateway(name="demoGateway")
public interface DemoGateway {
#Gateway(requestChannel = "gatewayRequestChannel",replyChannel = "nullChannel")
void accept(Message<String> request);
}
Code-2:
#MessagingGateway(name="demoGateway",defaultRequestChannel =
"gatewayRequestChannel",defaultReplyChannel = "nullChannel")
public interface DemoGateway {
void accept(Message<String> request);
}
Yes. You are right. You can do approach 2 and leave the single method that confirms to the default configuration of #MessagingGateway without annotation.
However in practice, I will only move the truly default values to the MessagingGateway and leave other values to #Gateway annotation.
This is because it makes life and readability easier in the future if you have to add more methods to DemoGateway in the future.

Is there an equivalent of a "beforeHandler" for spring 4?

I have a controller with a requestmapping..
#Controller
public class TestController {
private static final String template = "Hello there, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/hello")
public #ResponseBody String hello() {
return "Hello";
}
}
How can I make it such that everytime a user goes to a RequestMapping, or whichever url, some other method is called to println to the console the URL the user is at before it actually enters the method "hello"?
I would like to use this method to ensure users have proper credentials in the future. I see there are #PreAuthorize annotation, but there doesnt seem to be a method associated with it that I can write my own logic, i.e. do a simple println to the console with the current URL the user is at.
You have a number of options.
With Spring, you can implement and register a HandlerInterceptor and implement its preHandle method to log the request URL, which you can reconstruct with the various HttpServletRequest methods.
With pure servlet-api, you can implement and register your own Filter which logs the request URL and then continues the chain, with doFilter(..).
You can also use AOP, advise all your #RequestMapping annotated methods with a #Before advice that logs the URL.

Resources