Skip to content

Commit a3b92fd

Browse files
committed
chore: refactor spring-boot tests and implement DataFetcher tests for upload
1 parent 3930e57 commit a3b92fd

File tree

11 files changed

+272
-65
lines changed

11 files changed

+272
-65
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.auritylab.graphql.kotlin.toolkit.spring
2+
3+
import com.auritylab.graphql.kotlin.toolkit.spring.api.GraphQLInvocation
4+
import graphql.ExecutionInput
5+
import graphql.ExecutionResult
6+
import graphql.GraphQL
7+
import org.springframework.web.context.request.WebRequest
8+
import java.util.concurrent.CompletableFuture
9+
10+
class SyncGQLInvocation(
11+
private val gql: GraphQL
12+
) : GraphQLInvocation {
13+
override fun invoke(data: GraphQLInvocation.Data, request: WebRequest): CompletableFuture<ExecutionResult> {
14+
return CompletableFuture.completedFuture(
15+
gql.execute(
16+
ExecutionInput.newExecutionInput()
17+
.query(data.query)
18+
.operationName(data.operationName)
19+
.variables(data.variables ?: mapOf())
20+
.build()
21+
)
22+
)
23+
}
24+
}
Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
package com.auritylab.graphql.kotlin.toolkit.spring
22

3-
import com.auritylab.graphql.kotlin.toolkit.spring.api.GraphQLInvocation
43
import com.auritylab.graphql.kotlin.toolkit.spring.api.schemaOfResourceFiles
54
import com.fasterxml.jackson.databind.ObjectMapper
65
import com.fasterxml.jackson.module.kotlin.KotlinModule
7-
import com.nhaarman.mockitokotlin2.any
8-
import com.nhaarman.mockitokotlin2.whenever
9-
import graphql.ExecutionResultImpl
10-
import org.mockito.Mockito
116
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
127
import org.springframework.context.annotation.Bean
138
import org.springframework.context.annotation.Configuration
149
import org.springframework.context.annotation.Import
15-
import org.springframework.context.annotation.Profile
1610
import org.springframework.web.servlet.config.annotation.EnableWebMvc
17-
import java.util.concurrent.CompletableFuture
1811

1912
@Configuration
2013
@EnableWebMvc
@@ -26,14 +19,4 @@ internal class TestConfiguration {
2619

2720
@Bean
2821
fun schema() = schemaOfResourceFiles("schemas/schema.graphqls")
29-
30-
@Bean
31-
@Profile("graphql-invocation-mock")
32-
fun invocation(): GraphQLInvocation {
33-
val m = Mockito.mock(GraphQLInvocation::class.java)
34-
35-
whenever(m.invoke(any(), any())).thenReturn(CompletableFuture.completedFuture(ExecutionResultImpl(listOf())))
36-
37-
return m
38-
}
3922
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.auritylab.graphql.kotlin.toolkit.spring
2+
3+
import me.lazmaid.kraph.Kraph
4+
5+
object TestOperations {
6+
val getUserQuery = Kraph {
7+
query {
8+
fieldObject("getUser") {
9+
field("id")
10+
field("name")
11+
field("surname")
12+
}
13+
}
14+
}.toGraphQueryString()
15+
16+
val createUserMutation_withoutUpload = Kraph {
17+
mutation {
18+
//val uploadVar = variable("upload", "Upload", "")
19+
fieldObject("createUser", args = mapOf("name" to "test", "surname" to "test")) {
20+
field("id")
21+
field("name")
22+
field("surname")
23+
}
24+
}
25+
}.toGraphQueryString()
26+
27+
val createUserMutation_withUpload = Kraph {
28+
mutation {
29+
val uploadVar = variable("upload", "Upload", "")
30+
fieldObject("createUser", args = mapOf("name" to "test", "surname" to "test", "upload" to uploadVar)) {
31+
field("id")
32+
field("name")
33+
field("surname")
34+
}
35+
}
36+
}.toGraphQueryString()
37+
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.auritylab.graphql.kotlin.toolkit.spring.controller
22

33
import com.auritylab.graphql.kotlin.toolkit.spring.TestConfiguration
4-
import com.auritylab.graphql.kotlin.toolkit.spring.api.GraphQLInvocation
54
import com.fasterxml.jackson.databind.ObjectMapper
6-
import com.nhaarman.mockitokotlin2.clearInvocations
7-
import me.lazmaid.kraph.Kraph
8-
import org.junit.jupiter.api.BeforeEach
95
import org.springframework.beans.factory.annotation.Autowired
106
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
117
import org.springframework.test.annotation.DirtiesContext
128
import org.springframework.test.context.ContextConfiguration
139
import org.springframework.test.web.servlet.MockMvc
1410

11+
/**
12+
* Implements the abstract for all controller tests. This provides some based beans and utility functions to
13+
* simplify work with the operations.
14+
*/
1515
@DirtiesContext
1616
@AutoConfigureMockMvc
1717
@ContextConfiguration(classes = [TestConfiguration::class])
@@ -22,27 +22,6 @@ internal abstract class AbstractControllerTest {
2222
@Autowired
2323
protected lateinit var objectMapper: ObjectMapper
2424

25-
@Autowired
26-
protected lateinit var invocation: GraphQLInvocation
27-
28-
@BeforeEach
29-
fun resetMock() {
30-
clearInvocations(invocation)
31-
}
32-
/**
33-
* Will create a GraphQL Query for testing purpose.
34-
*/
35-
protected fun query(): String =
36-
Kraph {
37-
query {
38-
fieldObject("getUser") {
39-
field("id")
40-
field("name")
41-
field("surname")
42-
}
43-
}
44-
}.toGraphQueryString()
45-
4625
/**
4726
* Will create a JSON encoded GraphQL body. The [query] must be given, [operationName] and [variables] are optional.
4827
*/
@@ -64,5 +43,5 @@ internal abstract class AbstractControllerTest {
6443
* Will return a [ByteArray] which contains a file for testing purpose.
6544
*/
6645
protected fun file(): ByteArray =
67-
javaClass.classLoader.getResourceAsStream("test_file.png").readAllBytes()
46+
javaClass.classLoader.getResourceAsStream("test_file.png")!!.readAllBytes()
6847
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.auritylab.graphql.kotlin.toolkit.spring.controller
2+
3+
import com.auritylab.graphql.kotlin.toolkit.spring.SyncGQLInvocation
4+
import com.auritylab.graphql.kotlin.toolkit.spring.api.GraphQLInvocation
5+
import com.auritylab.graphql.kotlin.toolkit.spring.provided.ProvidedScalars
6+
import com.nhaarman.mockitokotlin2.any
7+
import com.nhaarman.mockitokotlin2.whenever
8+
import graphql.GraphQL
9+
import graphql.schema.DataFetcher
10+
import graphql.schema.GraphQLScalarType
11+
import graphql.schema.idl.FieldWiringEnvironment
12+
import graphql.schema.idl.ScalarWiringEnvironment
13+
import graphql.schema.idl.WiringFactory
14+
import org.mockito.Mockito
15+
import org.springframework.beans.factory.annotation.Autowired
16+
import org.springframework.beans.factory.annotation.Qualifier
17+
import org.springframework.boot.test.context.TestConfiguration
18+
import org.springframework.context.annotation.Bean
19+
import org.springframework.context.annotation.Primary
20+
import org.springframework.context.annotation.Profile
21+
import org.springframework.test.context.ActiveProfiles
22+
23+
/**
24+
* Abstract implementation of a controller test which verifies against a data fetcher. This will create a single
25+
* mocked [DataFetcher] bean with the according [WiringFactory]. The mocked [DataFetcher] can be obtained through
26+
* [dataFetcher].
27+
*/
28+
@ActiveProfiles("data-fetcher-test")
29+
internal abstract class AbstractDataFetcherControllerTest : AbstractControllerTest() {
30+
@TestConfiguration
31+
class Configuration {
32+
@Bean
33+
@Primary
34+
@Profile("data-fetcher-test")
35+
fun syncInvocation(gql: GraphQL): GraphQLInvocation = SyncGQLInvocation(gql)
36+
37+
@Bean()
38+
fun dataFetcher(): DataFetcher<*> {
39+
val m = Mockito.mock(DataFetcher::class.java)
40+
41+
// Always return null.
42+
whenever(m.get(any())).then {
43+
println("tt")
44+
45+
null
46+
}
47+
48+
return m
49+
}
50+
51+
@Bean
52+
@Primary
53+
fun customWiringFactory(@Qualifier("dataFetcher") df: DataFetcher<*>): WiringFactory {
54+
return object : WiringFactory {
55+
override fun providesScalar(environment: ScalarWiringEnvironment): Boolean {
56+
// For testing purpose we need to add the Upload scalar.
57+
return environment.scalarTypeDefinition.name == "Upload"
58+
}
59+
60+
override fun getScalar(environment: ScalarWiringEnvironment): GraphQLScalarType {
61+
if (environment.scalarTypeDefinition.name == "Upload")
62+
return ProvidedScalars.upload
63+
64+
throw IllegalStateException()
65+
}
66+
67+
override fun providesDataFetcher(environment: FieldWiringEnvironment): Boolean {
68+
// As we cover all tests with the createUser mutation, we just simply the DataFetcher for this field.
69+
return environment.fieldDefinition.name == "createUser"
70+
}
71+
72+
override fun getDataFetcher(environment: FieldWiringEnvironment): DataFetcher<*> {
73+
if (environment.fieldDefinition.name == "createUser")
74+
return df
75+
76+
throw IllegalStateException()
77+
}
78+
}
79+
}
80+
}
81+
82+
@Autowired
83+
@Qualifier("dataFetcher")
84+
lateinit var dataFetcher: DataFetcher<*>
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.auritylab.graphql.kotlin.toolkit.spring.controller
2+
3+
import com.auritylab.graphql.kotlin.toolkit.spring.api.GraphQLInvocation
4+
import com.nhaarman.mockitokotlin2.any
5+
import com.nhaarman.mockitokotlin2.clearInvocations
6+
import com.nhaarman.mockitokotlin2.whenever
7+
import graphql.ExecutionResultImpl
8+
import org.junit.jupiter.api.BeforeEach
9+
import org.mockito.Mockito
10+
import org.mockito.internal.util.MockUtil
11+
import org.springframework.beans.factory.annotation.Autowired
12+
import org.springframework.boot.test.context.TestConfiguration
13+
import org.springframework.context.annotation.Bean
14+
import org.springframework.context.annotation.Primary
15+
import org.springframework.context.annotation.Profile
16+
import org.springframework.test.context.ActiveProfiles
17+
import java.util.concurrent.CompletableFuture
18+
19+
/**
20+
* Abstract implementation of a controller test which verifies against the invocation. This will create a mocked
21+
* [GraphQLInvocation] bean. The mocked instance can be obtained with [invocation]. The invocations will rest
22+
* before each test.
23+
*/
24+
@ActiveProfiles("invocation-test")
25+
internal abstract class AbstractInvocationControllerTest : AbstractControllerTest() {
26+
@TestConfiguration
27+
class Configuration {
28+
@Bean
29+
@Primary
30+
@Profile("invocation-test")
31+
fun mockedInvocation(): GraphQLInvocation {
32+
val m = Mockito.mock(GraphQLInvocation::class.java)
33+
34+
whenever(
35+
m.invoke(
36+
any(),
37+
any()
38+
)
39+
).thenReturn(CompletableFuture.completedFuture(ExecutionResultImpl(listOf())))
40+
41+
return m
42+
}
43+
}
44+
45+
@Autowired
46+
protected lateinit var invocation: GraphQLInvocation
47+
48+
@BeforeEach
49+
fun resetMock() {
50+
// Just to be sure the instance is the mock.
51+
if (MockUtil.isMock(invocation))
52+
clearInvocations(invocation)
53+
}
54+
}

graphql-kotlin-toolkit-spring-boot/src/test/kotlin/com/auritylab/graphql/kotlin/toolkit/spring/controller/GetControllerTest.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
package com.auritylab.graphql.kotlin.toolkit.spring.controller
22

3+
import com.auritylab.graphql.kotlin.toolkit.spring.TestOperations
34
import com.nhaarman.mockitokotlin2.any
45
import com.nhaarman.mockitokotlin2.argThat
56
import com.nhaarman.mockitokotlin2.times
67
import com.nhaarman.mockitokotlin2.verify
78
import org.junit.jupiter.api.Test
89
import org.springframework.boot.test.context.SpringBootTest
9-
import org.springframework.test.context.ActiveProfiles
1010
import org.springframework.test.web.servlet.get
1111
import java.util.UUID
1212

13-
@ActiveProfiles("graphql-invocation-mock")
1413
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
15-
internal class GetControllerTest : AbstractControllerTest() {
14+
internal class GetControllerTest : AbstractInvocationControllerTest() {
1615
@Test
1716
fun `(get) should call invocation correctly`() {
18-
val inputQuery = query()
17+
val inputQuery = TestOperations.getUserQuery
1918

2019
mvc.get("/graphql") {
2120
param("query", inputQuery)
@@ -28,7 +27,7 @@ internal class GetControllerTest : AbstractControllerTest() {
2827

2928
@Test
3029
fun `(get) should call invocation with variables correctly`() {
31-
val inputQuery = query()
30+
val inputQuery = TestOperations.getUserQuery
3231
val inputVariables = mapOf(Pair("name", "test"), Pair("surname", "test"))
3332

3433
mvc.get("/graphql") {
@@ -43,7 +42,7 @@ internal class GetControllerTest : AbstractControllerTest() {
4342

4443
@Test
4544
fun `(get) should call invocation with operation name correctly`() {
46-
val inputQuery = query()
45+
val inputQuery = TestOperations.getUserQuery
4746
val inputOperationName = UUID.randomUUID().toString()
4847

4948
mvc.get("/graphql") {
@@ -64,7 +63,7 @@ internal class GetControllerTest : AbstractControllerTest() {
6463

6564
@Test
6665
fun `(get) should handle nested variables correctly`() {
67-
val inputQuery = query()
66+
val inputQuery = TestOperations.getUserQuery
6867
val inputVariables = mapOf(
6968
Pair("name", "test"),
7069
Pair("surname", "test"),

0 commit comments

Comments
 (0)