spring-projects / spring-projects/spring-boot

Add simple API to JacksonTester for direct instantiation

Open
#26,906 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: on-hold type: enhancement
Dominant language
Java
Stars
81.5k
Forks
42.7k
Avg merge
2d 4h
Merged PRs (30d)
65

Description

@JsonTest and json testers are hidden gems for tests with json.

Background

I have seen many test classes that create their own JsonUtils to load json files.
The JSON testers in org.springframework.boot.test.json could do it and they are more powerful.

However, the creation of the json testers, for example, JacksonTester is somewhat geared toward the usage with @JsonTest.

When I want to use it outside of @JsonTest, instantiation requires reflection based instance injection or a bit verbose constructor APIs.

JacksonTester<Foo> tester;

@BeforeEach
void beforeEach(){
    // reflection based injection
    JacksonTester.initFields(this, objectMapper);
}
@Test
void myTest() {
  // it is not intuitive constructor to use
  JacksonTester<Foo> tester =  new JacksonTester<>(getClass(), ResolvableType.forClass(MySample.class), objectMappeer);
}

Proposal

To make direct instantiation simpler, it would be nice to add simple constructors or a builder.

For example, JacksonTester could have constructors that doesn't require resourceLoadClass argument:

// simple usage
JacksonTester(Class<T> type, ObjectMapper objectMapper)

// for fine tuning
JacksonTester(ResolvableType type, ObjectMapper objectMapper)

// for generics (above ResolvableType kind of covers it though)
JacksonTester(ParameterizedTypeReference<?> typeReference, ObjectMapper objectMapper)

Or with a builder:

// for simple class
JsonTester<MySample> tester = builder.type(MySamle.class).build();

// for collection
JsonTester<List<MySample>> tester = builder.forList(MySamle.class).build();

// for class with generics
JsonTester<Foo<Bar>> tester = builder.type(Foo.class, Bar.class).build();

This way, I can simply create an instance in a test method like:

@Test
void myTest() {
  JacksonTester<Foo> tester = new JacksonTester<>(Foo.class, objectMapper);
}

Implementation Details

The current public constructor takes:

  • Class<?> resourceLoadClass
  • ResolvableType type
  • ObjectMapper objectMapper

Within AbstractJsonMarshalTester, resourceLoadClass is enforced to be a non-null. However, it is essentially passed down to the ClassPathResource to get a classloader from the resourceLoadClass. In ClassPathResource, it is actually a nullable parameter. When the parameter is null, it falls back to the system class loader. I think in many cases it is fine, especially in test. Or, it could use the target type class to retrieve a classloader.

Passing ResolvableType as a constructor parameter is a bit unintuitive for direct instantiation. It could just take the target type class and internally convert it to a ResolvableType instance.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with JacksonTester and AbstractJsonMarshalTester, then inspect how the current resourceLoadClass and ResolvableType constructor are used; ClassPathResource provides the class-loader behavior described in the issue. Define and verify a direct-instantiation API that works outside @JsonTest, covering the selected simple and generic type cases without requiring a resource-load class.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.