Webflux 集成测试中出现 UnsupportedOperationException

问题描述 投票:0回答:1

我正在为我的

Webflux
服务中的以下端点编写集成测试

@GetMapping
public Mono<ResponseEntity<List<UserWebModel>>> getUsers() {
    Flux<User> users = loadUserService.getusers(getUserIdFromToken());

    return users.collectList()
        .map(userList -> ResponseEntity.ok(userMapper.map(userList)))
        .switchIfEmpty(Mono.just(ResponseEntity.status(HttpStatus.NOT_FOUND).build()));
}

当我在本地启动并测试服务时,此端点工作正常,但在我的集成测试中,我得到了

UnsupportedOperationException

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UserIntegrationTest  {

  @Autowired
  private WebTestClient webTestClient;

  @Autowired
  private UserRepository userRepository;

  @BeforeAll
  void setup() {
    userRepository.saveAll(createMockUsers()).subscribe();
  }

  @AfterAll()
  void cleanup() {
    userRepository.deleteAll().subscribe();
  }

  @Test
  void test_getUsers() {
    initWiremockStubs();   

    this.webTestClient
        .mutateWith(mockJwt().jwt(jwt -> jwt.claim("userid", "12345")))
        .get()
        .uri("/users")
        .exchange()
        .expectStatus().isOk();
  }

异常

[ERROR] [] o.s.w.s.a.HttpWebHandlerAdapter - [4024b710] Error [java.lang.UnsupportedOperationException] for HTTP GET "/users", but ServerHttpResponse already committed (200 OK)
Error occurred after response was completed: OK(200 OK)[Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json", Content-Length:"2", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Content-Type-Options:"nosniff", X-Frame-Options:"DENY", X-XSS-Protection:"0", Referrer-Policy:"no-referrer"]
org.springframework.web.reactive.function.client.WebClientRequestException: Error occurred after response was completed: OK(200 OK)[Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json", Content-Length:"2", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Content-Type-Options:"nosniff", X-Frame-Options:"DENY", X-XSS-Protection:"0", Referrer-Policy:"no-referrer"]
at app//org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:136)
... 258 more
Caused by: java.lang.UnsupportedOperationException
at org.springframework.http.ReadOnlyHttpHeaders.set(ReadOnlyHttpHeaders.java:108)

在调试时我发现,

EncoderHttpMessageWriter
正在尝试将
ContentLength
写入
ReadOnlyHttpHeaders
,这会导致
UnsupportedOperationException

if (inputStream instanceof Mono) {
    return body
        .singleOrEmpty()
        .switchIfEmpty(Mono.defer(() -> {
            message.getHeaders().setContentLength(0);
            return message.setComplete().then(Mono.empty());
        }))
        .flatMap(buffer -> {
            Hints.touchDataBuffer(buffer, hints, logger);
            message.getHeaders().setContentLength(buffer.readableByteCount()); // <--
            return message.writeWith(Mono.just(buffer)
                .doOnDiscard(DataBuffer.class, DataBufferUtils::release));
        })
        .doOnDiscard(DataBuffer.class, DataBufferUtils::release);
}

当我删除端点中的响应正文时,测试有效。这里会出现什么问题?

java spring spring-boot spring-webflux
1个回答
0
投票

可能有点晚了,但我想我分享一下。

我在 Spring WebFlux 应用程序中使用自定义

UnsupportedOperationException
时遇到了
OrderedWebFilter
。此问题是由于在提交响应后尝试修改 ReadOnlyHttpHeaders 引起的,由过滤器内的反应式流操作触发。

我有我的代码:

  override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
    val filter = chain.filter(exchange)

    return securityHandler
      .token()
      .flatMap { successHandler.onAuthenticationSuccess(WebFilterExchange(exchange, chain), it) }
      .then(filter)
      .switchIfEmpty { filter }
  }

我注意到

chain.filter(exchange)
中有一个额外的
switchIfEmpty
电话。

我还添加了

chain.filter(exchange)
电话检查。

最终代码是:

    val differ = Mono.defer {
      if (exchange.response.isCommitted) {
        Mono.empty()
      } else
        chain.filter(exchange)
    }
    return securityHandler
      .token()
      .flatMap { successHandler.onAuthenticationSuccess(WebFilterExchange(exchange, chain), it) }
      .then(differ)

© www.soinside.com 2019 - 2024. All rights reserved.