我有一个正在运行的 WebGraphQLInterceptor,它根据身份验证和 GraphQL 查询参数的组合进行一些授权检查。
我想编写单元测试来单独测试我的
CustomGraphQlInterceptor
,而不是作为我们正在构建的更大 servlet 的一部分。但我无法在测试期间执行代码。
简化代码:
@Component
class CustomGraphQlInterceptor : WebGraphQlInterceptor {
private val logger = LoggerFactory.getLogger(javaClass)
// you can ignore IDE errors about being unable to Autowire this bean, it will
// successfully autowire at runtime.
@Suppress("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private lateinit var externalAuthorizationService: ExternalAuthorizationService
override fun intercept(
request: WebGraphQlRequest,
chain: WebGraphQlInterceptor.Chain,
): Mono<WebGraphQlResponse> {
logger.info("Validating request: $request")
logger.info("Context is ${ReactiveSecurityContextHolder.getContext()}")
return ReactiveSecurityContextHolder.getContext().flatMap { securityContext ->
val authentication = securityContext.authentication
if (authentication is JwtAuthenticationToken) {
val externalId = authentication.tokenAttributes["externalId"] as? String
val queryVariable = request.variables["queryVariable"]
val isAuthorized: Boolean = externalAuthorizationService.isAuthorized(externalId, queryVariable)
if (!isAuthorized) {
return@flatMap Mono.error(IllegalAccessException("not authorized"))
}
return@flatMap chain.next(request)
}
}
我的测试是这样的:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@ExtendWith(SpringExtension::class)
@SpringBootTest(
classes = [DgsAutoConfiguration::class, TestConfig::class],
webEnvironment = WebEnvironment.RANDOM_PORT,
properties = ["spring.main.web-application-type=reactive", "spring.profiles.active=test"],
)
@TestExecutionListeners(
ReactorContextTestExecutionListener::class,
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
)
class SupplierGraphQlInterceptorTest {
@Autowired
private lateinit var customGraphQlInterceptor: CustomGraphQlInterceptor
@MockkBean
private lateinit var externalAuthorizationService: ExternalAuthorizationService
@BeforeEach
fun setupAuthorized() {
TestSecurityContextHolder.setAuthentication(
JwtAuthenticationToken(
Jwt(
"token",
Instant.now(),
Instant.MAX,
mapOf(
"alg" to "none",
),
mapOf(
"externalId" to "1",
),
),
),
)
}
@Test
fun testUserIsAuthorized() {
val request =
WebGraphQlRequest(
URI("http://localhost:8080/graphql"), // uri
HttpHeaders(CollectionUtils.toMultiValueMap(mapOf())), // headers
null, // cookies
null, // remote address
mapOf(), // attributes
mapOf( // body
"query" to "{someQuery{id name}}",
"operationName" to "POST",
"variables" to mapOf("queryVariable" to "ABC"),
),
"1", // id
null, // local
)
every {
authorizationService.isAuthorized(1, "ABC")
} returns true
val chain = mockk<WebGraphQlInterceptor.Chain>()
every { chain.next(any()) } returns Mono.just(mockk<WebGraphQlResponse>())
StepVerifier
.create(
supplierGraphQlInterceptor.intercept(request, chain),
).expectNextMatches { it is WebGraphQlResponse }
.verifyComplete()
}
}
失败了
java.lang.AssertionError:期望“expectNextMatches”失败(预期:onNext();实际:onComplete())
我是 DGS、Spring 和 kotlin 的新手,所以我确信我在这里至少做错了几件事。
我能够解决这个问题。使用
ReactorContextTestExecutionListener
是一个错误,这可以做得更干净:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@ExtendWith(SpringExtension::class)
@SpringBootTest(
classes = [DgsAutoConfiguration::class, TestConfig::class],
webEnvironment = WebEnvironment.RANDOM_PORT,
properties = ["spring.main.web-application-type=reactive", "spring.profiles.active=test"],
)
class CustomGraphQlInterceptorTest {
@Autowired
private lateinit var customGraphQlInterceptor: CustomGraphQlInterceptor
@MockkBean
private lateinit var externalAuthorizationService: ExternalAuthorizationService
@Test
fun testUserIsAuthorized() {
val request =
WebGraphQlRequest(
URI("http://localhost:8080/graphql"), // uri
HttpHeaders(CollectionUtils.toMultiValueMap(mapOf())), // headers
null, // cookies
null, // remote address
mapOf(), // attributes
mapOf( // body
"query" to "{someQuery{id name}}",
"operationName" to "POST",
"variables" to mapOf("queryVariable" to "ABC"),
),
"1", // id
null, // local
)
every {
authorizationService.isAuthorized(1, "ABC")
} returns true
val jwt =
JwtAuthenticationToken(
Jwt(
"token",
Instant.now(),
Instant.MAX,
mapOf(
"alg" to "none",
),
mapOf(
"externalId" to 1,
),
),
)
val securityContext: SecurityContext = mockk()
every { securityContext.authentication } returns jwt
val chain = mockk<WebGraphQlInterceptor.Chain>()
every { chain.next(any()) } returns Mono.just(mockk<WebGraphQlResponse>())
StepVerifier
.create(
supplierGraphQlInterceptor.intercept(request, chain).contextWrite(
ReactiveSecurityContextHolder
.withSecurityContext(Mono.just(securityContext)),
),
).expectNextMatches { it is WebGraphQlResponse }
.verifyComplete()
}
}