How JSON Serialization Can Supercharge Your Java Unit Tests

This article explains how leveraging JSON serialization can dramatically simplify and accelerate Java unit test development by reducing boilerplate code for data mocking and verification, offering practical examples, naming conventions, resource organization, and integration tips for backend projects.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
How JSON Serialization Can Supercharge Your Java Unit Tests

Preface

"If a worker wants to do a good job, he must first sharpen his tools." In July this year the author took over several Java backend projects, quickly increased unit‑test coverage to over 70% by adding test cases, and attributes the speed to JSON serialization.

The author systematically summarizes techniques for using JSON serialization in Java unit tests.

1. Verbose Unit‑Test Code

Verbose Data Mocking Code

Mocking Class Property Values

Map<Long, String> languageMap = new HashMap<>(MapHelper.DEFAULT);
languageMap.put(1L, "Java");
languageMap.put(2L, "C++");
languageMap.put(3L, "Python");
languageMap.put(4L, "JavaScript");
... // dozens of lines
Whitebox.setInternalState(developmentService, "languageMap", languageMap);

Mocking Method Parameter Values

List<UserCreateVO> userCreateList = new ArrayList<>();
UserCreateVO userCreate0 = new UserCreateVO();
userCreate0.setName("Changyi");
userCreate0.setTitle("Java Developer");
... // dozens of lines
userCreateList.add(userCreate0);
UserCreateVO userCreate1 = new UserCreateVO();
userCreate1.setName("Tester");
userCreate1.setTitle("Java Tester");
... // dozens of lines
userCreateList.add(userCreate1);
... // dozens of items
userService.batchCreate(userCreateList);

Mocking Method Return Values

Long companyId = 1L;
List<UserDO> userList = new ArrayList<>();
UserDO user0 = new UserDO();
user0.setId(1L);
user0.setName("Changyi");
user0.setTitle("Java Developer");
... // dozens of lines
userList.add(user0);
UserDO user1 = new UserDO();
user1.setId(2L);
user1.setName("Tester");
user1.setTitle("Java Tester");
... // dozens of lines
userList.add(user1);
... // dozens of items
Mockito.doReturn(userList).when(userDAO).queryByCompanyId(companyId);

Verbose Data Verification Code

Validate Method Return Values

Long companyId = 1L;
List<UserVO> userList = userService.queryByCompanyId(companyId);
UserVO user0 = userList.get(0);
Assert.assertEquals("name mismatch", "Changyi", user0.getName());
Assert.assertEquals("title mismatch", "Java Developer", user0.getTitle());
... // dozens of lines
UserVO user1 = userList.get(1);
Assert.assertEquals("name mismatch", "Tester", user1.getName());
Assert.assertEquals("title mismatch", "Java Tester", user1.getTitle());
... // dozens of lines

Validate Method Parameter Values

ArgumentCaptor<List<UserDO>> userCreateListCaptor = CastUtils.cast(ArgumentCaptor.forClass(List.class));
Mockito.verify(userDAO).batchCreate(userCreateListCaptor.capture());
List<UserDO> userCreateList = userCreateListCaptor.getValue();
UserDO userCreate0 = userCreateList.get(0);
Assert.assertEquals("name mismatch", "Changyi", userCreate0.getName());
Assert.assertEquals("title mismatch", "Java Developer", userCreate0.getTitle());
... // dozens of lines
UserDO userCreate1 = userCreateList.get(1);
Assert.assertEquals("name mismatch", "Tester", userCreate1.getName());
Assert.assertEquals("title mismatch", "Java Tester", userCreate1.getTitle());
... // dozens of lines

2. Simplify with JSON Serialization

Using JSON serialization, the previous verbose code can be replaced with concise statements.

Simplify Data Mocking Code

Mocking Class Property Values

String text = ResourceHelper.getResourceAsString(getClass(), path + "languageMap.json");
Map<Long, String> languageMap = JSON.parseObject(text, new TypeReference<Map<Long, String>>() {});
Whitebox.setInternalState(mobilePhoneService, "languageMap", languageMap);

Example languageMap.json content:

{1:"Java",2:"C++",3:"Python",4:"JavaScript",...}

Mocking Method Parameter Values

String text = ResourceHelper.getResourceAsString(getClass(), path + "userCreateList.json");
List<UserCreateVO> userCreateList = JSON.parseArray(text, UserCreateVO.class);
userService.batchCreate(userCreateList);

Example userCreateList.json content:

[{"name":"Changyi","title":"Java Developer",...},{"name":"Tester","title":"Java Tester",...},...]

Mocking Method Return Values

Long companyId = 1L;
String text = ResourceHelper.getResourceAsString(getClass(), path + "userList.json");
List<UserDO> userList = JSON.parseArray(text, UserDO.class);
Mockito.doReturn(userList).when(userDAO).queryByCompanyId(companyId);

Example userList.json content:

[{"id":1,"name":"Changyi","title":"Java Developer",...},{"id":2,"name":"Tester","title":"Java Tester",...},...]

Simplify Data Verification Code

Validate Method Return Values

Long companyId = 1L;
List<UserVO> userList = userService.queryByCompanyId(companyId);
String text = ResourceHelper.getResourceAsString(getClass(), path + "userList.json");
Assert.assertEquals("User list mismatch", text, JSON.toJSONString(userList));

Validate Method Parameter Values

ArgumentCaptor<List<UserDO>> userCreateListCaptor = CastUtils.cast(ArgumentCaptor.forClass(List.class));
Mockito.verify(userDAO).batchCreate(userCreateListCaptor.capture());
String text = ResourceHelper.getResourceAsString(getClass(), path + "userCreateList.json");
Assert.assertEquals("User creation list mismatch", text, JSON.toJSONString(userCreateListCaptor.getValue()));

3. Test Case and Resource Naming

Test Class Naming

Test classes should start with the class under test and end with Test, e.g., UserServiceTest. Place them in src/test/java under the same package.

Test Method Naming

Methods should start with test followed by the method name, e.g., testBatchCreate. For multiple cases, add descriptive suffixes like WithSuccess, WithFailure, or WithException.

Test Resource Directory Naming

Resource directories should start with test and end with the class name, e.g., testUserService. They can be placed either alongside the test class in src/test/java or in src/test/resources. Method‑level resource directories follow the test method name, e.g., testUserService/testBatchCreateWithSuccess.

Test Resource File Naming

Use the variable or parameter name with a .json suffix, e.g., userCreateList.json. Ensure the file name matches the variable used in code.

Git Long‑Path Issue

If resource directory names exceed 50 characters, Git may report "Filename too long". Reduce the length or enable long paths with git config --system core.longpaths true.

4. JSON Resource File Usage Cases

Example Test Case

@RunWith(PowerMockRunner.class)
public class UserServiceTest {
    @Mock private UserDAO userDAO;
    @Mock private IdGenerator idGenerator;
    @InjectMocks private UserService userService;
    private static final String RESOURCE_PATH = "testUserService/";
    @Before public void beforeTest() {
        Whitebox.setInternalState(userService, "canModify", Boolean.TRUE);
    }
    @Test public void testCreateUserWithCreate() {
        Mockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString());
        Long userId = 1L;
        Mockito.doReturn(userId).when(idGenerator).next();
        String path = RESOURCE_PATH + "testCreateUserWithCreate/";
        String text = ResourceHelper.getResourceAsString(getClass(), path + "userCreateVO.json");
        UserVO userCreate = JSON.parseObject(text, UserVO.class);
        Assert.assertEquals("User ID mismatch", userId, userService.createUser(userCreate));
        Mockito.verify(userDAO).getIdByName(userCreate.getName());
        Mockito.verify(idGenerator).next();
        ArgumentCaptor<UserDO> userCreateCaptor = ArgumentCaptor.forClass(UserDO.class);
        Mockito.verify(userDAO).create(userCreateCaptor.capture());
        text = ResourceHelper.getResourceAsString(getClass(), path + "userCreateDO.json");
        Assert.assertEquals("User creation data mismatch", text, JSON.toJSONString(userCreateCaptor.getValue()));
        Mockito.verifyNoMoreInteractions(idGenerator, userDAO);
    }
    // ... other test methods omitted for brevity
}

Resource Directory Illustration

Resource directory structure
Resource directory structure

5. Sources of JSON Resource Files

Manually Assembled

[{"name":"Changyi","title":"Java Developer",...},{"name":"Tester","title":"Java Tester",...},...]

Generated by Code

public static void main(String[] args) {
    List<UserCreateVO> userCreateList = new ArrayList<>();
    UserCreateVO userCreate0 = new UserCreateVO();
    userCreate0.setName("Changyi");
    userCreate0.setTitle("Java Developer");
    // ... dozens of lines
    userCreateList.add(userCreate0);
    UserCreateVO userCreate1 = new UserCreateVO();
    userCreate1.setName("Tester");
    userCreate1.setTitle("Java Tester");
    // ... dozens of lines
    userCreateList.add(userCreate1);
    System.out.println(JSON.toJSONString(userCreateList));
}

Extracted from Logs

2021-08-31 18:55:40,867 INFO [UserService.java:34] - Query result for companyId(1): [{"id":1,"name":"Changyi","title":"Java Developer",...},{"id":2,"name":"Tester","title":"Java Tester",...},...]

Generated by Integration Tests

@RunWith(PandoraBootRunner.class)
public class UserDaoTest {
    @Resource private UserDAO userDAO;
    @Test public void testQueryByCompanyId() {
        Long companyId = 1L;
        List<UserDO> userList = userDAO.queryByCompanyId(companyId);
        log.info("userList={}", JSON.toJSONString(userList));
    }
}

6. JSON Serialization Techniques (Fastjson)

Serialize Objects, Arrays, Collections, Maps

String text = JSON.toJSONString(user);
String text = JSON.toJSONString(userArray);
String text = JSON.toJSONString(userList);
String text = JSON.toJSONString(userMap, SerializerFeature.MapSortField);

Serialize Selected Fields

SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
filter.getIncludes().addAll(Arrays.asList("id", "name"));
String text = JSON.toJSONString(user, filter);

Exclude Fields

SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
filter.getExcludes().addAll(Arrays.asList("gmtCreate", "gmtModified"));
String text = JSON.toJSONString(user, filter);

Custom Serializers

SerializeConfig.getGlobalInstance().put(Geometry.class, new GeometrySerializer());
String text = JSON.toJSONString(geometry);

7. JSON Deserialization Techniques (Fastjson)

Deserialize Objects, Arrays, Collections, Maps

UserVO user = JSON.parseObject(text, UserVO.class);
UserVO[] users = JSON.parseObject(text, UserVO[].class);
List<UserVO> userList = JSON.parseArray(text, UserVO.class);
Map<Long, UserVO> userMap = JSON.parseObject(text, new TypeReference<Map<Long, UserVO>>() {});

Deserialize with Non‑Public Fields

UserVO user = JSON.parseObject(text, UserVO.class, Feature.SupportNonPublicField);

Deserialize Builder‑Pattern Classes

User user = JSON.parseObject(text, User.UserBuilder.class, Feature.SupportNonPublicField).build();

Custom Deserializers

ParserConfig.getGlobalInstance().putDeserializer(Geometry.class, new GeometryDeserializer());
Geometry geometry = JSON.parseObject(text, Geometry.class);

8. When JSON Serialization Is Unnecessary

For simple pass‑through parameters or return values, or when only a few fields of an object are needed, adding JSON conversion adds overhead without benefit.

9. Advanced Mockito Uses with JSON

Mock Multiple Return Values

String text = ResourceHelper.getResourceAsString(getClass(), path + "recordList.json");
Record[] records = JSON.parseObject(text, Record[].class);
Mockito.doReturn(records[0], ArrayUtils.subarray(records, 1, records.length)).when(recordReader).read();

Mock Return Values Based on Arguments

String text = ResourceHelper.getResourceAsString(getClass(), path + "roleMap.json");
Map<Long, String> roleIdMap = JSON.parseObject(text, new TypeReference<Map<Long, String>>() {});
Mockito.doAnswer(invocation -> roleIdMap.get(invocation.getArgument(0))).when(roleService).get(Mockito.anyLong());

Verify Multiple Calls with JSON Comparison

ArgumentCaptor<UserCreateVO> captor = ArgumentCaptor.forClass(UserCreateVO.class);
Mockito.verify(userDAO, Mockito.atLeastOnce()).create(captor.capture());
String expected = ResourceHelper.getResourceAsString(getClass(), path + "userCreateList.json");
Assert.assertEquals("User creation list mismatch", expected, JSON.toJSONString(captor.getAllValues()));
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJSONMockingTest Resources
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.