graphql-java-kickstart / graphql-spring-boot

GraphQL and GraphiQL Spring Framework Boot Starters - Forked from oembedler/graphql-spring-boot due to inactivity.
https://www.graphql-java-kickstart.com/spring-boot/
MIT License
1.5k stars 325 forks source link

Spring Boot and JUnit 5 doesn't work #132

Closed alexcibotari closed 6 years ago

alexcibotari commented 6 years ago

Hello After updating to the latest version, I'm not able to run tests. Spring Boot : 2.0.5 JUnit : 5.1.1 Graphql-Spring-Boot : 5.x.x I use workaround with RANDOM_PORT, but without any success

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

Error appear on next line of codes , usually when I try to get Request data

RequestContextHolder.getRequestAttributes()
SecurityContextHolder.getContext().getAuthentication()
oliemansm commented 6 years ago

Are you using @GraphQLTest or something else? Can you share a working minimum example to reproduce this?

alexcibotari commented 6 years ago

After adding graphql-spring-test, I'm getting errors like Can't find more Jackson ObjectMapper

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1506)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1101)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:818)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:724)
    ... 70 more

with Random PORT

Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:204)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:178)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152)
    ... 61 more
oliemansm commented 6 years ago

Can you share an example please to reproduce this. There was a fix in v5.0.4 regarding the ObjectMapper.

alexcibotari commented 6 years ago

All works fine after update graphqlToolVersion = '5.3.3' graphqlSpringBootVersion = '5.0.4'

THX

alexcibotari commented 6 years ago

Hello @oliemansm ,

I have again the same issue from issue description with graphqlToolVersion = "5.3.5" graphqlSpringBootVersion = "5.0.6"

my test class

@ExtendWith(SpringExtension::class)
@SpringBootTest(classes = [Application::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(ProfileConstants.TEST)
class CatalogOptionsServiceTest(

after adding @GraphQLTest

Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:204)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:178)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152)
    ... 49 more
oliemansm commented 6 years ago

@alexcibotari That's correct, see the release notes for spring-boot 5.0.6: https://github.com/graphql-java-kickstart/graphql-spring-boot/releases/tag/v5.0.6.

I haven't been able to find a solution that satisfies all use cases.

osahner commented 5 years ago

@oliemansm still no solution or workaround?

oliemansm commented 5 years ago

Unfortunately not. I've tried multiple approaches at the time, but wasn't able to find a satisfactory solution as indicated and couldn't afford to spend more time on it. Would be great if there's anybody else that does have the time and is able to find a proper solution.

So right now unfortunately you have to add this annotation to the unit test class:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

as described in these release notes: https://github.com/graphql-java-kickstart/graphql-spring-boot/releases/tag/v5.0.6

osahner commented 5 years ago

Tried already with no success.

<junit-jupiter.version>5.4.0</junit-jupiter.version>
<graphql-java.version>11.0</graphql-java.version>
<graphql-java-tools.version>5.2.4</graphql-java-tools.version>
<graphql-spring-boot-starter.version>5.7.0</graphql-spring-boot-starter.version>
package ***
import com.graphql.spring.boot.test.GraphQLTest
import com.graphql.spring.boot.test.GraphQLTestTemplate
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.skyscreamer.jsonassert.JSONAssert
import org.skyscreamer.jsonassert.JSONCompareMode
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@GraphQLTest
class GraphqlServiceApplicationTest(@Autowired private val graphQLTestTemplate: GraphQLTestTemplate) {
  @Test
  fun version() {
    val expected = """
{
  "data": {
    "version": "1.0.0"
  }
}
    """.trimIndent()
    val result = graphQLTestTemplate.postForResource("{ version }")

    assertNotNull(result)
    assertEquals(HttpStatus.OK, result.statusCode)
    JSONAssert.assertEquals(expected, result.rawResponse.body, JSONCompareMode.LENIENT)
  }
}
oliemansm commented 5 years ago

This line is incorrect:

val result = graphQLTestTemplate.postForResource("{ version }")

postForResource is when you have a file on your classpath that contains the actual query. If you want to send the query directly as the parameter you should be using post("{ version }") instead.

Looked at some of my own tests using JUnit 5 and realized you don't need to add that annotation manually after all. It is automatically configured when using @GraphQLTest annotation.

You haven't posted the actual error message or stack trace you're facing, but looking at the code that method call is most likely the cause at the moment.

osahner commented 5 years ago

.post() is private 😄 please make it public.

With some modifications it did work (somehow)

First I had to add:

  @Bean
  fun servletWebServerFactory(): ServletWebServerFactory {
    return TomcatServletWebServerFactory()
  }

to my config to avoid the servlet startup problem.

Second I modified the test:

@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class GraphqlServiceApplicationTest(
  @Autowired private val restTemplate: TestRestTemplate
) {
  @Test
  fun version() {
    val expected = """
{
  "data": {
    "version": "1.0.0"
  }
}
    """.trimIndent()
    val headers = HttpHeaders()
    headers.contentType = MediaType.APPLICATION_JSON_UTF8
    val request = HttpEntity("""{"query":"{version}","variables":null}""", headers)
    val result = GraphQLResponse(restTemplate.exchange("/graphql", HttpMethod.POST, request, String::class))

    assertNotNull(result)
    assertEquals(HttpStatus.OK, result.statusCode)
    JSONAssert.assertEquals(expected, result.rawResponse.body, JSONCompareMode.LENIENT)
  }
}

I couldn't get it to work with graphQLTestTemplate.postMultipart("{ version }", null) because of a strange error:

...
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
 at [Source: (BufferedInputStream); line: 1, column: 10] (through reference chain: graphql.servlet.internal.GraphQLRequest["query"])
...
java.lang.IllegalArgumentException: json string can not be null or empty
    at com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:383)
    at com.jayway.jsonpath.internal.ParseContextImpl.parse(ParseContextImpl.java:36)
    at com.jayway.jsonpath.JsonPath.parse(JsonPath.java:599)
    at com.graphql.spring.boot.test.GraphQLResponse.<init>(GraphQLResponse.java:22)
    at com.graphql.spring.boot.test.GraphQLTestTemplate.postRequest(GraphQLTestTemplate.java:117)
    at com.graphql.spring.boot.test.GraphQLTestTemplate.postMultipart(GraphQLTestTemplate.java:108)
osahner commented 5 years ago

215 fixes the graphQLTestTemplate Bug.