📜 ⬆️ ⬇️

We test Spring Rest controllers: simpler, shorter, more reliable. Spring Security Test + JSON Matcher



Hello!
Own JSON Matcher, the use of Spring Security Test , recently included in Spring Security 4.0, and the abandonment of transactions when testing services can make tests easier and more reliable.

I used this approach when creating an application on my Topjava course (Maven / Spring / Security / JPA (Hibernate) / Rest (Jackson) / Bootstrap (CSS) / jQuery + plugins), the source code of the project can be taken on github .
')
This article is not another Spring REST Controller Testing Tutorial and assumes that you are already familiar with it.

I already wrote about the benefits of non-transactional transactions in a previous publication In the footsteps of Spring Pet Clinic. Maven / Spring Context / Spring Test / Spring ORM / Spring Data JPA . To the listed disadvantages of using @Transaction, you can still add the inability to view real requests when executing the service method to the database (for example, if hibernate.use_sql_comments is enabled)

Here I will write as easier, shorter, more reliable to test Spring REST controllers.

Spring security test


Prior to the release of Spring Security 4.0, it was a separate project that was included in Spring Security 4.0 as Improved Testing Support.
Now it is in the central maven repository and connects like this:
<properties> <spring-security.version>4.0.1.RELEASE</spring-security.version> </properties><dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <version>${spring-security.version}</version> </dependency> 

General code for testing controllers can be made an abstract class .
Connecting security to Spring MVC tests has become a bit easier:
  mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .apply(springSecurity()).build(); 

For REST services, the project uses http-basic, whose support in Spring Security Test is among many others.
For convenience, we create test data and make httpBasic connection even easier:
 public static RequestPostProcessor userHttpBasic(User user) { return SecurityMockMvcRequestPostProcessors .httpBasic(user.getEmail(), user.getPassword()); } mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(ADMIN))) .andExpect(... 

At the same time, in tests, there is no substitution of the security context, as you had to do before , but the httpBasic "Authorization" header is honestly set.

Checking the JSON content of the response through your own ResultMatcher


The usual practice in all REST testing guides is: use the json-path to selectively check the content fields of the json response (there is usually not enough patience for a full check). Or the use of more advanced libraries , which does not change the essence of the approach.

However, it is much better to compare at once the object with all the nested levels of the hierarchy! Since The string representation of JSON objects cannot be compared (it may have different formatting and field order), you need to serialize the contents of the JSON response into an object and compare objects. Spring MVC is integrated with the Jackson JSON library , so we can configure ObjectMapper as convenient for us (for example, disable serialization of lazy loading of Hibernate object fields ) and make a convenient utility class for JSON serialization-deserialization .

For use in Spring Test syntax tests:
 .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) 

We make our own ResultMatcher with the methods contentMatcher and contentListMatcher comparing objects and a list of objects, respectively. It is enough to configure this Matcher to work with the necessary objects (in the simplest case, if the ORM object has the equals method set to PK and it cannot be used for comparison, you can compare objects using toString ):
 public static final ModelMatcher<UserMeal, String> MATCHER = new ToStringModelMatcher<>(UserMeal.class); 

For more complex cases, for comparison, you will have to make a wrapper over an ORM object .
But now the tests for checking the JSON object in the REST response with any level of attachments of the JSON object will look very simple:

 mockMvc.perform(get(REST_URL + "by?email=" + USER.getEmail()) .with(userHttpBasic(ADMIN))) .andExpect(MATCHER.contentMatcher(USER)) .andExpect(... 

The REST tests of the project controllers are configured for in-memory hsqldb, so they can be run without connecting to the DB.
I hope that these solutions will be useful to you and thank you for your attention.

Source: https://habr.com/ru/post/259055/


All Articles