Spring Boot - Testing JSON Serialization and Deserialization With @JsonTest - Example
In this section, we will learn how to test JSON Serialization and Deserialization With @JsonTest in Spring Boot application.
1. @JsonTest
@JsonTest annotation can be used to test slices of our application and the auto-configuration that it import by default are listed below:
- org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
- org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration
- org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
- org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
- org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration
Testing JSON Serialization and Deserialization Example
2. Creating a spring boot application
First, open the Spring initializr https://start.spring.io
Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact json-example. Here I selected the Maven project - language Java 17 - Spring Boot 3.1.5, and Spring Web.
Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(jsontest-example) file and downloads the project. Then, Extract the Zip file.
Then, import the project on your favourite IDE.
Final Project Directory:
Complete pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>jsontest-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jsontest-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latest</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-test starter will provide following libraries:
- JUnit
- Spring Test & Spring Boot Test
- AssertJ
- Hamcrest
- Mockito
- JSONassert
- JsonPath
Create UserDTO
package com.knf.dev.demo.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDate;
import java.util.Map;
public class UserDTO {
private String name;
private String email;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate dob;
private Map<String, String> roles;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDate getDob() {
return dob;
}
public void setDob(LocalDate dob) {
this.dob = dob;
}
public Map<String, String> getRoles() {
return roles;
}
public void setRoles(Map<String, String> roles) {
this.roles = roles;
}
}
Create user.json inside resource directory
{
"name":"Sibin",
"email":"sibin@gmail.com",
"dob":"1970-01-09",
"roles":{
"role1":"Admin",
"role2":"Editor"
}
}
We are using this JSON data for our testing purposes later.
JsontestExampleApplication.java
package com.knf.dev.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JsontestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(JsontestExampleApplication.class, args);
}
}
Write Unit test for JSON Serialization and Deserialization
Let’s write our test cases by creating UserDTOTests class
Create UserDTOTests
When using JUnit 4, this annotation should be used in combination with @RunWith(SpringRunner.class). But for this example we are using JUnit 5, there’s no need to add the equivalent @ExtendWith(SpringExtension.class).
package com.knf.dev.demo;
import com.knf.dev.demo.dto.UserDTO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonContent;
import org.springframework.boot.test.json.ObjectContent;
import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.time.Month;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@JsonTest
public class UserDTOTests {
@Autowired
private JacksonTester<UserDTO> jacksonTester;
@Value("classpath:user.json")
Resource userResource;
@Test
void serializeInCorrectFormat() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setEmail("sibin@gmail.com");
userDTO.setName("Sibin");
LocalDate dob = LocalDate.of(1970, Month.JANUARY, 9);
userDTO.setDob(dob);
Map<String,String> roles= new HashMap<>();
roles.put("role1","Admin");
roles.put("role2","Editor");
userDTO.setRoles(roles);
JsonContent<UserDTO> json = jacksonTester.write(userDTO);
// Assert against a `user.json` file
assertThat(json).isEqualToJson(userResource);
// JSON path based assertions
assertThat(json).hasJsonPathStringValue("@.email");
assertThat(json).extractingJsonPathStringValue("@.dob")
.isEqualTo("1970-01-09");
assertThat(json).extractingJsonPathStringValue("@.name")
.isEqualTo("Sibin");
assertThat(json).extractingJsonPathMapValue("@.roles").
hasFieldOrProperty("role1");
assertThat(json).extractingJsonPathMapValue("@.roles").
extractingByKey("role1").isEqualTo("Admin");
}
@Test
void deserializeFromCorrectFormat() throws Exception {
//Convert Resource to String
String json = StreamUtils.copyToString(userResource.getInputStream(),
Charset.defaultCharset());
UserDTO userDTO = jacksonTester.parseObject(json);
assertThat(userDTO.getEmail()).isEqualTo("sibin@gmail.com");
assertThat(userDTO.getName()).isEqualTo("Sibin");
assertThat(userDTO.getDob()).isEqualTo("1970-01-09");
assertThat(userDTO.getRoles().size()).isEqualTo(2);
}
}
- JacksonTester is a AssertJ based JSON tester backed by Jackson. If you are using Gson or Jsonb, you have to use its sibling GsonTester or JsonbTester.
- Here JsonContent is created from a JSON tester. jacksonTester.write(userDTO) returns JsonContent.
- assertThat is used to check the specified value matches the expected value. It will accept the two parameters, the first contains the actual value, and the second will have the object matching the condition.
- hasJsonPathStringValue() method is used to check that the actual value at the given JSON path produces a non-null string result.
- extractingJsonPathStringValue() is used to extract the string value at the given JSON path.
- extractingJsonPathMapValue() method is used extract the map value at the given JSON path.
- extractingByKey() method is used to extract the value of given key from the map under test.
- parseObject() is used to return the object created from parsing the specific JSON String.
3. Run the test
Or you can run the test using following command:
mvn test -Dtest=UserDTOTests