From 3fab7a5875a2ace4a7895b44e101de0d0df17235 Mon Sep 17 00:00:00 2001 From: Simone Conte Date: Sat, 22 Nov 2025 12:01:24 +0000 Subject: [PATCH 1/2] Add singleElement() to GraphQlTester.EntityList singleElement() is a utility method to check for lists of size one, and return the Entity in the same statement. See gh-1380 Signed-off-by: Simone Conte --- .../test/tester/DefaultGraphQlTester.java | 12 ++++++++ .../graphql/test/tester/GraphQlTester.java | 7 +++++ .../test/tester/GraphQlTesterTests.java | 29 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java index d3c3ab41..00d16f65 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java @@ -655,6 +655,18 @@ public EntityList hasSizeGreaterThan(int size) { getEntity().size() > size)); return this; } + + @Override + public Entity singleElement() { + this.hasSize(1); + + E element = this.get().get(0); + @SuppressWarnings("unchecked") + Class elementClass = (Class) element.getClass(); + + return new DefaultPath(DefaultPath.this.basePath, DefaultPath.this.path + "[0]", DefaultPath.this.delegate) + .entity(elementClass); + } } diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java index a98f2b1b..66dec174 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java @@ -470,6 +470,13 @@ interface EntityList extends Entity, EntityL */ EntityList hasSizeGreaterThan(int size); + /** + * Verify the list has a single element and return an {@code Entity} spec for it. + *

This is a convenience method that combines {@link #hasSize(int) hasSize(1)} + * with navigating to the first element in the list. + * @return an {@code Entity} spec for the single element that allows further assertions + */ + Entity singleElement(); } /** diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java index 62e291c3..fdeb55e6 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java @@ -213,6 +213,35 @@ void entityList() { .containsExactly(han, leia); assertThat(getActualRequestDocument()).contains(document); + + assertThatThrownBy(() -> entityList.singleElement()) + .as("Should have exactly one element") + .hasMessage("Expecting list " + + "[MovieCharacter[name='Han Solo'], MovieCharacter[name='Leia Organa']] " + + "at path 'me.friends' to have size == 1\n" + + "Request: document='{me {name, friends}}'"); + } + + @Test + void entityListWithOneElement() { + + String document = "{me {name, friends}}"; + getGraphQlService().setDataAsJson(document, + "{" + + " \"me\":{" + + " \"name\":\"Luke Skywalker\"," + + " \"friends\":[{\"name\":\"Han Solo\"}]" + + " }" + + "}"); + + GraphQlTester.Response response = graphQlTester().document(document).execute(); + + MovieCharacter han = MovieCharacter.create("Han Solo"); + + response.path("me.friends") + .entityList(MovieCharacter.class) + .singleElement() + .isEqualTo(han); } @Test From f05e5976d220a6106a9138a697306454942bfe8e Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 21 Apr 2026 09:03:00 +0200 Subject: [PATCH 2/2] Polishing contribution Closes gh-1380 Signed-off-by: Brian Clozel --- .../test/tester/DefaultGraphQlTester.java | 2 - .../test/tester/GraphQlTesterTests.java | 55 +++++++++++++------ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java index 00d16f65..7c1fce01 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java @@ -659,11 +659,9 @@ public EntityList hasSizeGreaterThan(int size) { @Override public Entity singleElement() { this.hasSize(1); - E element = this.get().get(0); @SuppressWarnings("unchecked") Class elementClass = (Class) element.getClass(); - return new DefaultPath(DefaultPath.this.basePath, DefaultPath.this.path + "[0]", DefaultPath.this.delegate) .entity(elementClass); } diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java index fdeb55e6..5eae00fd 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java @@ -173,12 +173,14 @@ void entityList() { String document = "{me {name, friends}}"; getGraphQlService().setDataAsJson(document, - "{" + - " \"me\":{" + - " \"name\":\"Luke Skywalker\"," - + " \"friends\":[{\"name\":\"Han Solo\"}, {\"name\":\"Leia Organa\"}]" + - " }" + - "}"); + """ + { + "me":{ + "name":"Luke Skywalker", + "friends":[{"name":"Han Solo"}, {"name":"Leia Organa"}] + } + } + """); GraphQlTester.Response response = graphQlTester().document(document).execute(); @@ -213,26 +215,45 @@ void entityList() { .containsExactly(han, leia); assertThat(getActualRequestDocument()).contains(document); + } + + @Test + void singleElementShouldThrowWhenMultiple() { - assertThatThrownBy(() -> entityList.singleElement()) + String document = "{me {name, friends}}"; + getGraphQlService().setDataAsJson(document, + """ + { + "me":{ + "name":"Luke Skywalker", + "friends":[{"name":"Han Solo"}, {"name":"Leia Organa"}] + } + } + """); + + GraphQlTester.Response response = graphQlTester().document(document).execute(); + GraphQlTester.EntityList entityList = response.path("me.friends").entityList(MovieCharacter.class); + assertThatThrownBy(entityList::singleElement) .as("Should have exactly one element") .hasMessage("Expecting list " + - "[MovieCharacter[name='Han Solo'], MovieCharacter[name='Leia Organa']] " + - "at path 'me.friends' to have size == 1\n" + - "Request: document='{me {name, friends}}'"); + "[MovieCharacter[name='Han Solo'], MovieCharacter[name='Leia Organa']] " + + "at path 'me.friends' to have size == 1\n" + + "Request: document='{me {name, friends}}'"); } @Test - void entityListWithOneElement() { + void entityListWithSingleElement() { String document = "{me {name, friends}}"; getGraphQlService().setDataAsJson(document, - "{" + - " \"me\":{" + - " \"name\":\"Luke Skywalker\"," - + " \"friends\":[{\"name\":\"Han Solo\"}]" + - " }" + - "}"); + """ + { + "me":{ + "name":"Luke Skywalker", + "friends":[{"name":"Han Solo"}] + } + } + """); GraphQlTester.Response response = graphQlTester().document(document).execute();