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

Convert ArrayNode to List #654

Closed
bartsimp opened this issue Jul 28, 2022 · 4 comments · Fixed by #664
Closed

Convert ArrayNode to List #654

bartsimp opened this issue Jul 28, 2022 · 4 comments · Fixed by #664

Comments

@bartsimp
Copy link

bartsimp commented Jul 28, 2022

I'd like to convert a json array into a java List, but I got this error:

Error converting parameter at index 0: No implicit conversion to convert object of type com.fasterxml.jackson.databind.node.ArrayNode to type java.util.List
org.junit.jupiter.api.extension.ParameterResolutionException: Error converting parameter at index 0: No implicit conversion to convert object of type com.fasterxml.jackson.databind.node.ArrayNode to type java.util.List

Here my test:

@ParameterizedTest
@JsonClasspathSource("items.json")
void test1(@Property("items") List<Integer> items) {
    assertFalse(items.isEmpty());
    assertEquals(2, items.size());
}

@ParameterizedTest
@JsonClasspathSource("items.json")
void test2(@Property("items") Integer[] items) {
    assertEquals(2, items.length);
}

items.json:

[
  {
    "items": [ 1, 3 ]
  },
  {
    "items": [ 2, 4 ]
  }
]

what am I missing?

@Michael1993
Copy link
Member

Hi @bartsimp!

Unfortunately, I don't have a lot of time right now, but I wanted to call your attention to this issue:
#639

Your problem is in the JSON file.

@beatngu13
Copy link
Member

Hey @bartsimp!

We use Jackson as our JSON parser. Jackson creates an ArrayNode from items, which implements Iterable:

@ParameterizedTest
@JsonSource("""
		[
		  {
			"items": [ 1, 3 ]
		  },
		  {
			"items": [ 2, 4 ]
		  }
		]
		""")
void test1(@Property("items") Iterable<Integer> items) {
	assertThat(items).hasSize(2);
}

Alternatively, as suggested by @Michael1993, you can also modify your JSON:

@ParameterizedTest
@JsonSource("""
		[
		  [ 1, 3 ],
		  [ 2, 4 ]
		]
		""")
void test2(List<Integer> items) {
	assertThat(items).hasSize(2);
}

Feel free to reopen this issue if that doesn't help you.

@bartsimp
Copy link
Author

bartsimp commented Aug 1, 2022

@Michael1993, @beatngu13 Thanks for suggestions but I'm afraid is not exactly what I'm looking for...
I'd like to test a method like

public Integer sum(List<Integer> items) {
    return items.stream().reduce(0, Integer::sum);
}

using a json parameterized test which contains also the expected response

@ParameterizedTest
    @JsonSource("""
          [
                {
                  "items": [1, 3],
                  "expectedResult": 4
                },
                {
                  "items": [2, 4],
                  "expectedResult": 6
                }
          ]
          """)
    void testSum(
            @Property("items") List<Integer> items,
            @Property("expectedResult") Integer expectedResult
    ) {
        assertThat(
                new Calculator().sum(items),
                equalTo(expectedResult)
        );
    }

do you think is possible?

@beatngu13
Copy link
Member

@bartsimp I actually made a mistake, my suggestion above throws the following exception once you dig a little deeper:

java.lang.ClassCastException: class com.fasterxml.jackson.databind.node.IntNode cannot be cast to class java.lang.Integer

A workaround (I don't like it though):

@ParameterizedTest
@JsonSource("""
		[
			  {
				"items": [1, 3],
				"expectedResult": 4
			  },
			  {
				"items": [2, 4],
				"expectedResult": 6
			  }
		]
		"""
)
void testSum(
		@Property("items") Iterable<IntNode> items,
		@Property("expectedResult") int expectedResult
) {
	List<Integer> itemsList = StreamSupport.stream(items.spliterator(), false)
			.map(IntNode::intValue)
			.toList();
	Integer actualResult = new Calculator().sum(itemsList);
	assertEquals(expectedResult, actualResult);
}

The "problem" is this method:

https://github.com/junit-pioneer/junit-pioneer/blob/main/src/main/java/org/junitpioneer/jupiter/json/JacksonNode.java#L64-L88

No case matches, which is why we return node (JsonNode / ArrayNode with IntNodes).

I reopen this issue. I'm not that familiar with the Jackson API, and I'm not sure how type erasure will be a problem here, but I believe we can do better.

@beatngu13 beatngu13 reopened this Aug 9, 2022
@beatngu13 beatngu13 self-assigned this Aug 9, 2022
Michael1993 added a commit that referenced this issue Sep 26, 2022
The initial implementation of the JSON sources for ParameterizedTest
was missing the functionality to map from JSON to more complex Java
types like List. This commit fixes that by using Jackson's
ObjectMapper and JavaType.

Closes: #654 
PR: #664
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants