├── .editorconfig ├── .github └── workflows │ └── build.yml ├── .gitignore ├── README.md ├── build.gradle.kts ├── example.gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── kotlin │ └── com │ └── symbaloo │ └── graphql │ └── test │ └── GraphQLKotlinTestDsl.kt └── test ├── kotlin └── com │ └── symbaloo │ └── graphql │ └── test │ └── GraphQLKotlinTestDslTest.kt └── resources └── query.graphql /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | tab_width = 4 8 | indent_style = space 9 | 10 | [{*.kt, *.kts}] 11 | max_line_length = 120 12 | ij_smart_tabs = true 13 | ij_continuation_indent_size = 4 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: OrangeLabs-moe/gradle-actions@v5.0-openjdk-11 13 | with: 14 | args: build 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | 4 | .gradle 5 | /build/ 6 | 7 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 8 | !gradle-wrapper.jar 9 | 10 | gradle.properties 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kotlin GraphQL Test DSL for graphql-java 2 | ======================================== 3 | 4 | This is a kotlin DSL to easily write (integration) tests for your [graphql-java](https://github.com/graphql-java/graphql-java) 5 | application. 6 | 7 | It is inspired by the [Spring MockMVC DSL](https://docs.spring.io/spring/docs/current/spring-framework-reference/languages.html#mockmvc-dsl) 8 | and lets you use [JsonPath](https://github.com/json-path/JsonPath) to quickly retrieve results from the response. 9 | 10 | 11 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.symbaloo/graphql-kotlin-test-dsl/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.symbaloo/graphql-kotlin-test-dsl) 12 | ![Actions](https://github.com/arian/graphql-kotlin-test-dsl/workflows/CI/badge.svg) 13 | 14 | ### Gradle 15 | 16 | ```kotlin 17 | testImplementation("com.symbaloo:graphql-kotlin-test-dsl:1.0.9") 18 | ``` 19 | 20 | ### Maven 21 | 22 | ```xml 23 | 24 | com.symbaloo 25 | graphql-kotlin-test-dsl 26 | 1.0.9 27 | 28 | ``` 29 | 30 | Example 31 | ------- 32 | 33 | The following code shows an example how to use this test library. 34 | 35 | ```kotlin 36 | // the graphql-java schema 37 | val schema: GraphQL = createTestSchema() 38 | val result: ExecutionResult = graphQLTest(schema) { 39 | // define a query 40 | query( 41 | """ 42 | |query Init(${"$"}echo: String) { 43 | | echo(echo: ${"$"}echo) 44 | | hello { hello } 45 | |}""".trimMargin() 46 | ) 47 | // add a variable 48 | variable("echo", "response") 49 | } 50 | // check for noErrors 51 | .andExpect { noErrors() } 52 | // create json context 53 | .andExpectJson { 54 | // go into the result with a json path 55 | path("$.hello.hello") { 56 | // quick isEqualTo check 57 | isEqualTo("world") 58 | // do something with the result 59 | andDo { 60 | assertThat(it).isEqualTo("world") 61 | } 62 | } 63 | // combination of `path` and `andDo` 64 | pathAndDo("$.hello") { it: Map -> 65 | assertThat(it).contains("hello", "world") 66 | } 67 | 68 | // combination of `path` and `isEqualTo` 69 | pathIsEqualTo("$.echo", "response") 70 | 71 | // it can also return values 72 | val hello = pathAndDo("$.hello") { map: Map -> 73 | map["hello"] 74 | } 75 | assertThat(hello).isEqualTo("world") 76 | } 77 | .andReturn() 78 | ``` 79 | 80 | ### Link with your Assertion Library 81 | 82 | ```kotlin 83 | graphQLTest(createTestSchema()) { 84 | query("{ answer }") 85 | }.andExpectJson { 86 | assertPath("$.answer").isEqualTo(42) 87 | } 88 | 89 | fun GraphQLJsonResultMatcherDsl.assertPath(path: String): Assert = 90 | pathAndDo(path) { it: Any? -> assertThat(it) } 91 | ``` 92 | 93 | License 94 | ------- 95 | 96 | MIT License 97 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.21" 5 | id("com.diffplug.spotless") version "5.9.0" 6 | id("org.jetbrains.dokka") version "1.4.20" 7 | `java-library` 8 | `maven-publish` 9 | signing 10 | } 11 | 12 | group = "com.symbaloo" 13 | version = "1.0.9" 14 | description = "A Kotlin DSL to write Tests for graphql-java" 15 | val repoDescription = description 16 | val repoUrl = "https://github.com/arian/graphql-kotlin-test-dsl" 17 | 18 | val isReleaseVersion = !version.toString().endsWith("SNAPSHOT") 19 | 20 | repositories { 21 | mavenCentral() 22 | jcenter() 23 | } 24 | 25 | dependencies { 26 | api("com.graphql-java:graphql-java:15.+") 27 | implementation("com.jayway.jsonpath:json-path:2.5.+") 28 | implementation("com.google.code.gson:gson:2.8.6") 29 | implementation("org.opentest4j:opentest4j:1.2.0") 30 | implementation("org.slf4j:slf4j-api:1.7.+") 31 | testImplementation(kotlin("reflect")) 32 | testImplementation("org.slf4j:slf4j-simple:1.7.+") 33 | testImplementation("org.junit.jupiter:junit-jupiter:5.7.0") 34 | testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.23") 35 | } 36 | 37 | tasks { 38 | withType { 39 | kotlinOptions.jvmTarget = "1.8" 40 | } 41 | 42 | test { 43 | useJUnitPlatform() 44 | testLogging { 45 | events("passed", "skipped", "failed") 46 | } 47 | } 48 | 49 | register("sourcesJar") { 50 | archiveClassifier.set("sources") 51 | from(sourceSets.main.get().allSource) 52 | } 53 | 54 | javadoc { 55 | dependsOn("dokkaJavadoc") 56 | } 57 | 58 | register("javadocJar") { 59 | dependsOn("javadoc") 60 | archiveClassifier.set("javadoc") 61 | from("$buildDir/javadoc") 62 | } 63 | } 64 | 65 | spotless { 66 | kotlin { 67 | ktlint("0.40.0") 68 | } 69 | kotlinGradle { 70 | ktlint("0.40.0") 71 | } 72 | } 73 | 74 | publishing { 75 | publications { 76 | create("mavenJava") { 77 | from(components["kotlin"]) 78 | artifact(tasks["sourcesJar"]) 79 | artifact(tasks["javadocJar"]) 80 | 81 | versionMapping { 82 | usage("java-api") { 83 | fromResolutionOf("runtimeClasspath") 84 | } 85 | usage("java-runtime") { 86 | fromResolutionResult() 87 | } 88 | } 89 | 90 | pom { 91 | name.set("GraphQL Kotlin Test DLS") 92 | description.set(repoDescription) 93 | url.set(repoUrl) 94 | 95 | licenses { 96 | license { 97 | name.set("MIT") 98 | url.set("http://www.opensource.org/licenses/mit-license.php") 99 | distribution.set("repo") 100 | } 101 | } 102 | 103 | developers { 104 | developer { 105 | id.set("arian") 106 | name.set("Arian Stolwijk") 107 | email.set("arian@symbaloo.com") 108 | } 109 | } 110 | 111 | scm { 112 | connection.set("scm:git:git://github.com/arian/graphql-kotlin-test-dsl.git") 113 | url.set("https://github.com/arian/graphql-kotlin-test-dsl") 114 | } 115 | } 116 | } 117 | 118 | repositories { 119 | maven { 120 | 121 | val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2") 122 | val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots") 123 | url = if (isReleaseVersion) releasesRepoUrl else snapshotsRepoUrl 124 | 125 | // these can be set through gradle.properties 126 | if (properties.containsKey("mavenRepoUser")) { 127 | credentials { 128 | username = properties["sonatypeRepoUser"] as String? 129 | password = properties["sonatypeRepoPassword"] as String? 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | signing { 138 | setRequired { isReleaseVersion && gradle.taskGraph.hasTask("publish") } 139 | if (isReleaseVersion) { 140 | sign(publishing.publications["mavenJava"]) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /example.gradle.properties: -------------------------------------------------------------------------------- 1 | # https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials 2 | signing.keyId=abc 3 | signing.password=secret 4 | signing.secretKeyRingFile=/home/foobar/.gnupg/secring.gpg 5 | 6 | # https://central.sonatype.org/pages/gradle.html 7 | mavenRepoUser=user 8 | mavenRepoPassword=secret 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arian/graphql-kotlin-test-dsl/bbe712eb0b36b00dc19e36a4ada2f33a48ea9e52/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /src/main/kotlin/com/symbaloo/graphql/test/GraphQLKotlinTestDsl.kt: -------------------------------------------------------------------------------- 1 | package com.symbaloo.graphql.test 2 | 3 | import com.google.gson.GsonBuilder 4 | import com.jayway.jsonpath.JsonPath 5 | import com.jayway.jsonpath.PathNotFoundException 6 | import graphql.ExecutionInput 7 | import graphql.ExecutionResult 8 | import graphql.GraphQL 9 | import graphql.GraphQLError 10 | import org.opentest4j.AssertionFailedError 11 | 12 | /** 13 | * Initial function to kick of the test DSL with the GraphQL schema object 14 | */ 15 | fun graphQLTest(schema: GraphQL, tester: GraphQLQueryBuilderDsl.() -> Unit): GraphQLResultActionsDsl { 16 | return GraphQLQueryBuilderDsl(schema).apply(tester).execute() 17 | } 18 | 19 | class GraphQLQueryBuilderDsl( 20 | private val schema: GraphQL, 21 | internal var query: String = "" 22 | ) { 23 | 24 | internal val variables = mutableMapOf() 25 | internal var context: Any? = null 26 | internal var builder: ((ExecutionInput.Builder) -> Unit)? = null 27 | 28 | internal fun execute(): GraphQLResultActionsDsl { 29 | val executionResult = schema.execute { 30 | it.query(query) 31 | variables.takeIf { v -> v.isNotEmpty() }?.also { v -> it.variables(v) } 32 | context?.also { c -> it.context(c) } 33 | builder?.also { b -> b(it) } 34 | it 35 | } 36 | return GraphQLResultActionsDsl(executionResult) 37 | } 38 | } 39 | 40 | /** 41 | * Set the query to test 42 | */ 43 | fun GraphQLQueryBuilderDsl.query(query: String) { 44 | this.query = query 45 | } 46 | 47 | /** 48 | * Read .graphql query files from (test) resources 49 | */ 50 | fun GraphQLQueryBuilderDsl.queryFromFile(filename: String) { 51 | query( 52 | Thread.currentThread().contextClassLoader 53 | ?.getResourceAsStream(filename) 54 | .let { requireNotNull(it) } 55 | .use { String(it.readBytes()) } 56 | ) 57 | } 58 | 59 | /** 60 | * Add a variable 61 | */ 62 | fun GraphQLQueryBuilderDsl.variable(name: String, value: Any?) { 63 | this.variables[name] = value 64 | } 65 | 66 | /** 67 | * Add multiple variables 68 | */ 69 | fun GraphQLQueryBuilderDsl.variables(map: Map) { 70 | this.variables += map 71 | } 72 | 73 | /** 74 | * Set a context object 75 | */ 76 | fun GraphQLQueryBuilderDsl.context(context: Any) { 77 | this.context = context 78 | } 79 | 80 | /** 81 | * Use the graphql-java builder to set query inputs 82 | * @see ExecutionInput.Builder 83 | */ 84 | fun GraphQLQueryBuilderDsl.builder(builder: (ExecutionInput.Builder) -> Unit) { 85 | this.builder = builder 86 | } 87 | 88 | class GraphQLResultActionsDsl(internal val executionResult: ExecutionResult) 89 | 90 | /** 91 | * @return [GraphQLResultActionsDsl] for asserting and checking results 92 | */ 93 | fun GraphQLResultActionsDsl.andExpect(expectations: GraphQLResultMatcherDsl.() -> Unit): GraphQLResultActionsDsl { 94 | GraphQLResultMatcherDsl(executionResult).expectations() 95 | return this 96 | } 97 | 98 | /** 99 | * @return [GraphQLResultActionsDsl] for asserting and checking results 100 | */ 101 | fun GraphQLResultActionsDsl.andExpectJson( 102 | expectations: GraphQLJsonResultMatcherDsl.() -> Unit 103 | ): GraphQLResultActionsDsl { 104 | andExpect { json { expectations() } } 105 | return this 106 | } 107 | 108 | /** 109 | * Do something with the executing result 110 | * @see ExecutionResult 111 | */ 112 | fun GraphQLResultActionsDsl.andDo(action: (ExecutionResult) -> Unit): GraphQLResultActionsDsl { 113 | action(executionResult) 114 | return this 115 | } 116 | 117 | /** 118 | * Return the execution result 119 | * @see ExecutionResult 120 | */ 121 | fun GraphQLResultActionsDsl.andReturn(): ExecutionResult { 122 | return executionResult 123 | } 124 | 125 | /** 126 | * Return the data at a given path 127 | * @see ExecutionResult 128 | * @see GraphQLResultMatcherDsl.path 129 | */ 130 | fun GraphQLResultActionsDsl.andReturnPath(path: String): T { 131 | return JsonPathContext(executionResult.getData()).readJsonPathOrFail(path) 132 | } 133 | 134 | class GraphQLResultMatcherDsl(internal val executionResult: ExecutionResult) 135 | 136 | /** 137 | * Assert that there are no errors in the result 138 | */ 139 | fun GraphQLResultMatcherDsl.noErrors() { 140 | val errors = this.executionResult.errors 141 | if (errors.isNotEmpty()) { 142 | fail( 143 | """ 144 | |Expected no errors in the result. 145 | | 146 | |It got these errors: 147 | | 148 | |${errors.joinToString(">\n>\n") { it.message.prependIndent(">> ") }} 149 | |""".trimMargin(), 150 | emptyList(), 151 | errors 152 | ) 153 | } 154 | } 155 | 156 | /** 157 | * Assert that there is at least one error 158 | */ 159 | fun GraphQLResultMatcherDsl.hasError(matcher: GraphQLErrorResultMatcher.() -> Unit = { }) { 160 | val errors = this.executionResult.errors 161 | if (errors.isEmpty()) { 162 | fail( 163 | """ 164 | |Expected at least one error in the result. But there were no errors. 165 | |""".trimMargin(), 166 | null, 167 | errors 168 | ) 169 | } else { 170 | GraphQLErrorResultMatcher(errors).matcher() 171 | } 172 | } 173 | 174 | /** 175 | * Assert that some field has some value 176 | */ 177 | fun GraphQLResultMatcherDsl.rootFieldEqualTo(key: String, expected: T) { 178 | when (val data = executionResult.getData()) { 179 | is Map<*, *> -> { 180 | val actual = data[key] 181 | if (actual != expected) { 182 | fail("Expected field with key: $key", expected, actual) 183 | } 184 | } 185 | else -> { 186 | throw AssertionFailedError("Expected root data to be a map and contain field(s)") 187 | } 188 | } 189 | } 190 | 191 | /** 192 | * Parse the [ExecutionResult] data into a [GraphQLJsonResultMatcherDsl] DSL 193 | */ 194 | fun GraphQLResultMatcherDsl.json(fn: GraphQLJsonResultMatcherDsl.() -> R): R { 195 | val context = JsonPathContext(executionResult.getData()) 196 | return GraphQLJsonResultMatcherDsl(context).fn() 197 | } 198 | 199 | /** 200 | * Get a [GraphQLJsonPathResultMatcherDsl] DSL for a certain [JsonPath] path 201 | */ 202 | fun GraphQLResultMatcherDsl.path(jsonPath: String, fn: GraphQLJsonPathResultMatcherDsl.() -> R): R { 203 | return json { path(jsonPath, fn) } 204 | } 205 | 206 | fun GraphQLResultMatcherDsl.path(jsonPath: String, fn: GraphQLJsonPathResultMatcherDsl.() -> Unit): Unit = 207 | path(jsonPath, fn) 208 | 209 | /** 210 | * Test a value in the response 211 | */ 212 | fun GraphQLResultMatcherDsl.pathIsEqualTo(path: String, value: T) { 213 | return json { path(path) { isEqualTo(value) } } 214 | } 215 | 216 | /** 217 | * Be able to do something (e.g. assertions) with the value at the given path 218 | */ 219 | fun GraphQLResultMatcherDsl.pathAndDo(path: String, matcher: (T) -> R): R { 220 | return json { path(path) { andDo(matcher) } } 221 | } 222 | 223 | class GraphQLJsonResultMatcherDsl internal constructor(internal val context: JsonPathContext) 224 | 225 | /** 226 | * Use the [GraphQLJsonPathResultMatcherDsl] for a certain [JsonPath] path 227 | */ 228 | fun GraphQLJsonResultMatcherDsl.path(path: String, fn: GraphQLJsonPathResultMatcherDsl.() -> R): R { 229 | val value: T = context.readJsonPathOrFail(path) 230 | return GraphQLJsonPathResultMatcherDsl(path, context.data, value).fn() 231 | } 232 | 233 | fun GraphQLJsonResultMatcherDsl.path(path: String, fn: GraphQLJsonPathResultMatcherDsl.() -> Unit): Unit = 234 | path(path, fn) 235 | 236 | /** 237 | * Test the result of a [JsonPath] to be equal to a certain value 238 | */ 239 | fun GraphQLJsonResultMatcherDsl.pathIsEqualTo(path: String, expected: T): Unit = 240 | path(path) { isEqualTo(expected) } 241 | 242 | /** 243 | * Be able to do something (e.g. assertions) with the value at the given path 244 | */ 245 | fun GraphQLJsonResultMatcherDsl.pathAndDo(path: String, matcher: (T) -> R): R { 246 | return path(path) { andDo(matcher) } 247 | } 248 | 249 | /** 250 | * Returns the [ExecutionResult] as a JSON string 251 | */ 252 | fun GraphQLJsonResultMatcherDsl.doWithJsonString(fn: (String) -> R): R { 253 | return fn(context.json) 254 | } 255 | 256 | class GraphQLJsonPathResultMatcherDsl(internal val path: String, internal val data: Any, internal val value: T) 257 | 258 | /** 259 | * Do something with the result 260 | */ 261 | fun GraphQLJsonPathResultMatcherDsl.andDo(matcher: (T) -> R): R { 262 | return matcher(value) 263 | } 264 | 265 | fun GraphQLJsonPathResultMatcherDsl.isEqualTo(expected: T) { 266 | if (expected != value) { 267 | fail( 268 | """ 269 | | 270 | |Expected <$value> to be equal to <$expected> 271 | | 272 | |In path: $path 273 | | 274 | |In data: $data 275 | | 276 | """.trimMargin(), 277 | expected, 278 | value 279 | ) 280 | } 281 | } 282 | 283 | internal class JsonPathContext( 284 | internal val data: Any 285 | ) { 286 | internal val json = GsonBuilder().serializeNulls().create().toJson(data) 287 | private val context = JsonPath.parse(json) 288 | 289 | fun readJsonPathOrFail(path: String): T { 290 | return try { 291 | context.read(path) 292 | } catch (e: PathNotFoundException) { 293 | throw AssertionError("No results for path: $path\n\nIn data: $data") 294 | } 295 | } 296 | } 297 | 298 | class GraphQLErrorResultMatcher(internal val errors: List) 299 | 300 | fun GraphQLErrorResultMatcher.path(path: String, matcher: (GraphQLError) -> Unit = { }) { 301 | val parts = path.split(".") 302 | when (val error = errors.find { it.path == parts }) { 303 | null -> fail("Error with path '$path' couldn't be found", path, errors) 304 | else -> matcher(error) 305 | } 306 | } 307 | 308 | private fun fail(message: String, expected: Any?, value: Any?) { 309 | throw AssertionFailedError( 310 | "expected: <$expected> but was: <$value>", 311 | expected, 312 | value, 313 | AssertionError(message) 314 | ) 315 | } 316 | -------------------------------------------------------------------------------- /src/test/kotlin/com/symbaloo/graphql/test/GraphQLKotlinTestDslTest.kt: -------------------------------------------------------------------------------- 1 | package com.symbaloo.graphql.test 2 | 3 | import assertk.Assert 4 | import assertk.all 5 | import assertk.assertThat 6 | import assertk.assertions.contains 7 | import assertk.assertions.hasSize 8 | import assertk.assertions.isEqualTo 9 | import assertk.assertions.isFailure 10 | import assertk.assertions.isInstanceOf 11 | import assertk.assertions.isNotNull 12 | import assertk.assertions.isNull 13 | import assertk.assertions.key 14 | import assertk.assertions.prop 15 | import assertk.assertions.startsWith 16 | import graphql.ExceptionWhileDataFetching 17 | import graphql.ExecutionResult 18 | import graphql.GraphQL 19 | import graphql.schema.idl.RuntimeWiring.newRuntimeWiring 20 | import graphql.schema.idl.SchemaGenerator 21 | import graphql.schema.idl.SchemaParser 22 | import org.junit.jupiter.api.DisplayName 23 | import org.junit.jupiter.api.Nested 24 | import org.junit.jupiter.api.Test 25 | import org.junit.jupiter.api.assertThrows 26 | import org.opentest4j.AssertionFailedError 27 | 28 | private data class Bar(val foo: String) 29 | 30 | private val schema = """ 31 | |type Query { 32 | | hello: Bar 33 | | answer: Int 34 | | echo(echo: String): String 35 | | fromContext: String 36 | | list: [Bar!]! 37 | | error: Int 38 | |} 39 | |type Bar { 40 | | hello: String 41 | | infinite: Bar 42 | |} 43 | |""".trimMargin() 44 | 45 | class GraphQLKotlinTestDslTest { 46 | 47 | private fun createTestSchema(): GraphQL { 48 | val schemaParser = SchemaParser() 49 | val typeDefinitionRegistry = schemaParser.parse(schema) 50 | val runtimeWiring = newRuntimeWiring() 51 | .type("Query") { builder -> 52 | builder.dataFetcher("hello") { Bar("world") } 53 | builder.dataFetcher("answer") { 42 } 54 | builder.dataFetcher("echo") { it.arguments["echo"] } 55 | builder.dataFetcher("fromContext") { it.getContext().foo } 56 | builder.dataFetcher("list") { listOf(Bar("first"), Bar("second")) } 57 | builder.dataFetcher("error") { throw Exception("oops") } 58 | } 59 | .type("Bar") { builder -> 60 | builder.dataFetcher("hello") { it.getSource().foo } 61 | builder.dataFetcher("infinite") { 62 | val bar = it.getSource() 63 | bar.copy(foo = bar.foo.repeat(2)) 64 | } 65 | } 66 | .build() 67 | 68 | val schemaGenerator = SchemaGenerator() 69 | val graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring) 70 | return GraphQL.newGraphQL(graphQLSchema).build() 71 | } 72 | 73 | @Test 74 | fun `everything is fine`() { 75 | // the graphql-java schema 76 | val schema: GraphQL = createTestSchema() 77 | val result = graphQLTest(schema) { 78 | // define a query 79 | query( 80 | """ 81 | |query Init(${"$"}echo: String) { 82 | | echo(echo: ${"$"}echo) 83 | | hello { hello } 84 | |}""".trimMargin() 85 | ) 86 | // add a variable 87 | variable("echo", "response") 88 | } 89 | // check for noErrors 90 | .andExpect { noErrors() } 91 | // create json context 92 | .andExpectJson { 93 | // go into the result with a json path 94 | path("$.hello.hello") { 95 | // quick isEqualTo check 96 | isEqualTo("world") 97 | // do something with the result 98 | andDo { 99 | assertThat(it).isEqualTo("world") 100 | } 101 | } 102 | // combination of `path` and `andDo` 103 | pathAndDo("$.hello") { it: Map -> 104 | assertThat(it).contains("hello", "world") 105 | } 106 | 107 | // combination of `path` and `isEqualTo` 108 | pathIsEqualTo("$.echo", "response") 109 | 110 | // it can also return values 111 | val hello = pathAndDo("$.hello") { map: Map -> 112 | map["hello"] 113 | } 114 | assertThat(hello).isEqualTo("world") 115 | } 116 | .andReturn() 117 | 118 | assertThat(result.extensions).isNull() 119 | } 120 | 121 | @Test 122 | fun `assert library`() { 123 | graphQLTest(createTestSchema()) { 124 | query("{ answer }") 125 | }.andExpectJson { 126 | assertPath("$.answer").isEqualTo(42) 127 | } 128 | } 129 | 130 | @Nested 131 | inner class GraphQLQueryBuilderDsl { 132 | 133 | @Test 134 | fun `variable dsl`() { 135 | graphQLTest(createTestSchema()) { 136 | query("query X(\$echo: String) { echo(echo: \$echo) }") 137 | variable("echo", "world") 138 | }.andExpect { 139 | noErrors() 140 | pathIsEqualTo("\$.echo", "world") 141 | } 142 | } 143 | 144 | @Test 145 | fun `variables dsl`() { 146 | val q = """ 147 | |query X($${"echo"}: String, $${"e2"}: String) { 148 | | echo: echo(echo: $${"echo"}) 149 | | e2: echo(echo: $${"e2"}) 150 | |}""".trimMargin() 151 | graphQLTest(createTestSchema()) { 152 | query(q) 153 | variables(mapOf("echo" to "world", "e2" to "second")) 154 | }.andExpect { 155 | noErrors() 156 | pathIsEqualTo("\$.echo", "world") 157 | pathIsEqualTo("\$.e2", "second") 158 | } 159 | } 160 | 161 | @Test 162 | fun `context dsl`() { 163 | graphQLTest(createTestSchema()) { 164 | query("{ fromContext }") 165 | context(Bar("hey")) 166 | }.andExpect { 167 | noErrors() 168 | rootFieldEqualTo("fromContext", "hey") 169 | } 170 | } 171 | 172 | @Test 173 | fun `builder dsl`() { 174 | graphQLTest(createTestSchema()) { 175 | query("query X(\$echo: String) { fromContext echo(echo: \$echo) }") 176 | builder { input -> 177 | input.context(Bar("ctx")) 178 | input.variables(mapOf("echo" to "abc")) 179 | } 180 | }.andExpect { 181 | noErrors() 182 | rootFieldEqualTo("echo", "abc") 183 | rootFieldEqualTo("fromContext", "ctx") 184 | } 185 | } 186 | 187 | @Test 188 | fun `queryFromFile dsl`() { 189 | graphQLTest(createTestSchema()) { 190 | queryFromFile("./query.graphql") 191 | variable("echo", "abc") 192 | }.andExpect { 193 | noErrors() 194 | } 195 | } 196 | 197 | @Test 198 | fun `queryFromFile dsl can't find the file`() { 199 | assertThrows { 200 | graphQLTest(createTestSchema()) { 201 | queryFromFile("not-existing.graphql") 202 | variable("echo", "abc") 203 | } 204 | } 205 | } 206 | } 207 | 208 | @Nested 209 | @DisplayName("expect field to equal") 210 | inner class GraphQLResultActionsDsl { 211 | 212 | @Test 213 | fun andExpect() { 214 | graphQLTest(createTestSchema()) { 215 | query("{ answer }") 216 | }.andExpect { 217 | rootFieldEqualTo("answer", 42) 218 | } 219 | } 220 | 221 | @Test 222 | fun andDo() { 223 | graphQLTest(createTestSchema()) { 224 | query("{ answer }") 225 | }.andDo { 226 | assertThat(it.errors).hasSize(0) 227 | } 228 | } 229 | 230 | @Test 231 | fun `return execution result`() { 232 | val result = graphQLTest(createTestSchema()) { 233 | query("{ answer }") 234 | }.andReturn() 235 | 236 | assertThat(result).all { 237 | isInstanceOf(ExecutionResult::class.java) 238 | transform { it.getData>()["answer"] } 239 | .isEqualTo(42) 240 | } 241 | } 242 | 243 | @Test 244 | fun andReturnPath() { 245 | val result: String = graphQLTest(createTestSchema()) { 246 | query("{ hello { infinite { hello } } }") 247 | }.andReturnPath("$.hello.infinite.hello") 248 | assertThat(result).isEqualTo("worldworld") 249 | } 250 | 251 | @Test 252 | fun `andExpectJson shortcut`() { 253 | graphQLTest(createTestSchema()) { 254 | query("{ hello { infinite { hello } } }") 255 | }.andExpectJson { 256 | path("$.hello.infinite.hello") { 257 | isEqualTo("worldworld") 258 | } 259 | pathAndDo("$.hello") { map: Map -> 260 | assertThat(map).key("infinite").isNotNull() 261 | } 262 | } 263 | } 264 | } 265 | 266 | @Nested 267 | @DisplayName("expect field to equal") 268 | inner class GraphQLResultMatcherDsl { 269 | 270 | @Test 271 | fun `noError throws an exception for failed query`() { 272 | assertThrows { 273 | graphQLTest(createTestSchema()) { 274 | query("{ abc }") 275 | }.andExpect { noErrors() } 276 | } 277 | } 278 | 279 | @Test 280 | fun `expect field`() { 281 | graphQLTest(createTestSchema()) { 282 | query("{ answer }") 283 | }.andExpect { 284 | noErrors() 285 | rootFieldEqualTo("answer", 42) 286 | } 287 | } 288 | 289 | @Test 290 | fun `expect field is not equal`() { 291 | assertThrows { 292 | graphQLTest(createTestSchema()) { 293 | query("{ answer }") 294 | }.andExpect { 295 | rootFieldEqualTo("answer", "123") 296 | } 297 | } 298 | } 299 | 300 | @Test 301 | fun pathIsEqualTo() { 302 | graphQLTest(createTestSchema()) { 303 | query("{ hello { infinite { infinite { hello } } } }") 304 | }.andExpect { 305 | noErrors() 306 | pathIsEqualTo("$.hello.infinite.infinite.hello", "worldworldworldworld") 307 | } 308 | } 309 | 310 | @Test 311 | fun `pathIsEqualTo check with a list`() { 312 | graphQLTest(createTestSchema()) { 313 | query("{ list { hello } }") 314 | }.andExpect { 315 | noErrors() 316 | pathIsEqualTo("$.list.*.hello", listOf("first", "second")) 317 | } 318 | } 319 | 320 | @Test 321 | fun `json expectation`() { 322 | graphQLTest(createTestSchema()) { 323 | query("{ answer }") 324 | }.andExpect { 325 | noErrors() 326 | 327 | json { 328 | path("answer") { isEqualTo(42) } 329 | doWithJsonString { 330 | assertThat(it).isEqualTo("""{"answer":42}""") 331 | } 332 | } 333 | 334 | val result1 = json { pathAndDo("$.answer") { it: Int -> it * 2 } } 335 | assertThat(result1).isEqualTo(84) 336 | 337 | val result2 = json { path("$.answer") { andDo { it } } } 338 | assertThat(result2).isEqualTo(42) 339 | } 340 | } 341 | 342 | @Test 343 | fun `json serialize nulls`() { 344 | graphQLTest(createTestSchema()) { 345 | query( 346 | """ 347 | |query Init(${"$"}echo: String) { 348 | | echo(echo: ${"$"}echo) 349 | |}""".trimMargin() 350 | ) 351 | variable("echo", null) 352 | }.andExpect { 353 | noErrors() 354 | 355 | json { 356 | doWithJsonString { 357 | assertThat(it).isEqualTo("""{"echo":null}""") 358 | } 359 | path("$.echo") { 360 | isEqualTo(null) 361 | } 362 | 363 | pathIsEqualTo("$.echo", null) 364 | } 365 | } 366 | } 367 | 368 | @Test 369 | fun pathAndDo() { 370 | graphQLTest(createTestSchema()) { 371 | query("{ hello { infinite { hello } } }") 372 | }.andExpect { 373 | noErrors() 374 | pathAndDo("$.hello.infinite.hello") { it: String -> 375 | assertThat(it).isEqualTo("worldworld") 376 | } 377 | val result = pathAndDo("$.hello.infinite.hello") { it.length } 378 | assertThat(result).isEqualTo(10) 379 | } 380 | } 381 | 382 | @Test 383 | fun noErrors() { 384 | assertThat { 385 | graphQLTest(createTestSchema()) { query("{ error }") }.andExpect { noErrors() } 386 | }.isFailure() 387 | } 388 | 389 | @Test 390 | fun hasError() { 391 | graphQLTest(createTestSchema()) { query("{ error }") }.andExpect { hasError() } 392 | } 393 | } 394 | 395 | @Nested 396 | inner class GraphQLJsonPathResultMatcherDsl { 397 | 398 | @Test 399 | fun `json path andDo`() { 400 | graphQLTest(createTestSchema()) { 401 | query("{ hello { infinite { hello } } }") 402 | }.andExpect { 403 | noErrors() 404 | path("$.hello.infinite.hello") { 405 | val world = andDo { 406 | assertThat(it).isEqualTo("worldworld") 407 | it 408 | } 409 | assertThat("hello $world").isEqualTo("hello worldworld") 410 | } 411 | } 412 | } 413 | 414 | @Test 415 | fun `json path dsl isEqualTo`() { 416 | graphQLTest(createTestSchema()) { 417 | query("{ hello { infinite { infinite { hello } } } }") 418 | }.andExpect { 419 | noErrors() 420 | path("$.hello.infinite.infinite.hello") { 421 | isEqualTo("worldworldworldworld") 422 | } 423 | pathIsEqualTo("$.hello.infinite.infinite.hello", "worldworldworldworld") 424 | } 425 | } 426 | 427 | @Test 428 | fun `json path dsl isEqualTo error message`() { 429 | graphQLTest(createTestSchema()) { 430 | query("{ answer }") 431 | }.andExpect { 432 | noErrors() 433 | path("$.answer") { 434 | assertThat { isEqualTo(1) } 435 | .isFailure() 436 | .isInstanceOf(AssertionFailedError::class) 437 | .all { 438 | prop(AssertionFailedError::message) 439 | .isNotNull() 440 | .startsWith("expected: <1> but was: <42>") 441 | transform { it.actual.stringRepresentation }.isEqualTo("42") 442 | transform { it.expected.stringRepresentation }.isEqualTo("1") 443 | } 444 | } 445 | } 446 | } 447 | } 448 | 449 | @Nested 450 | inner class GraphQLErrorResultMatcher { 451 | 452 | @Test 453 | fun `assert error path`() { 454 | graphQLTest(createTestSchema()) { 455 | query("{ error }") 456 | } 457 | .andExpect { 458 | hasError { 459 | path("error") { error -> 460 | assertThat(error) 461 | .isInstanceOf(ExceptionWhileDataFetching::class) 462 | .transform { it.exception.message } 463 | .isEqualTo("oops") 464 | } 465 | } 466 | } 467 | } 468 | 469 | @Test 470 | fun `assert error of non-existing path`() { 471 | assertThat { 472 | graphQLTest(createTestSchema()) { 473 | query("{ error }") 474 | } 475 | .andExpect { 476 | hasError { 477 | path("error.bar") { } 478 | } 479 | } 480 | }.isFailure() 481 | } 482 | } 483 | } 484 | 485 | private fun GraphQLJsonResultMatcherDsl.assertPath(path: String): Assert = 486 | pathAndDo(path) { it: Any? -> assertThat(it) } 487 | -------------------------------------------------------------------------------- /src/test/resources/query.graphql: -------------------------------------------------------------------------------- 1 | query Init($echo: String) { 2 | echo(echo: $echo) 3 | hello { hello } 4 | } 5 | --------------------------------------------------------------------------------