Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonClassPathSource support generics type? #639

Closed
sherlockEix opened this issue May 21, 2022 · 5 comments
Closed

JsonClassPathSource support generics type? #639

sherlockEix opened this issue May 21, 2022 · 5 comments
Labels

Comments

@sherlockEix
Copy link

sherlockEix commented May 21, 2022

Hello. I've tried the demo below but it doesn't work. I think JsonClassPathSource can't deserialize json array to java list. Is there any other way to implement it? Thanks.

Java code:

@JsonClassPathSource("test.json")
void test(List<Person> persons){
  log.info("{}",persons)
}

Json file:

[
  {
    "name": "a",
    "age": 23
  },
  {
    "name": "a",
    "age": 23
  }
]
@Michael1993
Copy link
Member

Hi there @sherlockEix !

Just for the sake of completeness, could you also copy here your Person class and the exception message (I assume) you get when trying to execute the test above?

@sherlockEix
Copy link
Author

sherlockEix commented May 21, 2022

yes. this is my demo. i hope that jedisList can be deserialized from JEDIS path

  • java code
private static final String JEDIS = "org/junitpioneer/jupiter/json/jedis.json";
@ParameterizedTest
@JsonClasspathSource(JEDIS)
void listObject(List<Jedi> jedisList){
	System.out.println("jedisList = " + jedisList);
}
  • jedis content
[
  {
    "name": "Luke",
    "height": 172
  },
  {
    "name": "Yoda",
    "height": 66
  }
]
  • error
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<java.lang.Object>` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: UNKNOWN; byte offset: #UNKNOWN]

@Michael1993
Copy link
Member

Michael1993 commented May 21, 2022

Oh, yeah, I see where the problem is - you pass a list of objects to your test that expects a list of lists. See, when you execute a parameterized test, it expects an input (a list) which it converts to single parameters. In your example your parameter is a list, so the test tries to convert your input to a list but it can not do that because you supplied a simple list not a list of lists.

To demonstrate with an example, this should work:

@ParameterizedTest
@JsonClasspathSource(JEDI_LIST)
void listObject(List<Jedi> jedisList){
	System.out.println("jedisList = " + jedisList);
}

Jedi list:

[
  [
    {
      "name": "Luke",
      "height": 172
    },
    {
      "name": "Yoda",
      "height": 66
    }
  ]
]

Note that I used two square brackets [ ] in my JSON file - because I want to pass a list of lists.

@Michael1993
Copy link
Member

Michael1993 commented May 21, 2022

This actually makes me wonder why you would want to use a List as an input parameter. Do you want to test something on a collection of objects at the same time? Carefully examine your test - if you use a for loop or any other method to iterate and/or examine single objects in your passed List you might want to reconsider letting the ParameterizedTest do the iteration for you and just writing a test with a single object in mind.

Again to demonstrate with an example, let's say you want to test a performance review system. This approach would be wrong:

@ParameterizedTest
@EmployeeTeamsSource
void employeeScoresShouldExceedMinimum(List<Employee> employees) {
  for (Employee employee : employees) {
    assertThat(employee.performanceScore()).isGreaterThan(SCORE_MIN);
  }
}

In the above example you are testing individual objects, but you pass them in a list, which makes your test less readable. The correct approach in this case would be:

@ParameterizedTest
@EmployeeSource
void employeeScoresShouldExceedMinimum(Employee employee) {
  assertThat(employee.performanceScore()).isGreaterThan(SCORE_MIN);
}

Which is basically the same test, except it lets the test unwrap your collection. This isn't always the right approach, of course. For example, you might want to evaluate the weighted average of performance scores in teams (a collection of employees). Then passing a List would be a correct approach because you need information from multiple individual objects in a single test.

@ParameterizedTest
@EmployeeTeamsSource
void employeeScoresShouldExceedMinimum(List<Employee> employees) {
  assertThat(calculateWeightedAverage(employees)).isGreaterThan(SCORE_MIN);
}

@sherlockEix
Copy link
Author

i want use @JsonClasspathSource to prepare my integrate test data. i would like to write data to json file. it's a complex struct. maybe single object or list. then. i will use injected object(list) to do test. my first idea is i write a utils to load data and deserialize to object. but i found @JsonClasspathSource so i use it to prepare my complex test data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants