Groovy测试Spring Boot WebClient PUT请求触发空指针异常
问题分析与解决方案
核心问题
触发NullPointerException的原因有两个:
- Spock Mock交互规则冲突:你在
given块中定义了webClientMock.put() >> bodySpec存根,但又在then块中声明1 * webClientMock.put()进行调用次数验证。Spock的Mock默认是严格模式,这种分开的声明会导致存根被覆盖,实际调用时webClient.put()返回null。 - 方法存根不匹配:待测试Java代码中调用的是
body(Mono.just(...), CacheApiRequest.class),但测试代码里存根的是bodyValue(_),两者是不同的重载方法,存根无法命中,会导致后续链式调用返回null。
修复步骤
1. 调整Spock Mock的存根与验证方式
将存根和调用次数验证合并声明,或者确保存根在验证之前且逻辑一致。推荐合并声明,避免冲突。
2. 修正WebClient方法存根的匹配
将bodyValue的存根改为匹配实际调用的body(Publisher, Class)重载方法。
3. 补充静态方法Mock(如果需要)
如果AuthUtils.getFuncional和Utils.getFuncionalToogle是静态方法,需要额外Mock(比如用Mockito的静态Mock),否则会执行真实逻辑,可能影响测试流程。
修改后的测试代码示例
@Unroll def "must call putProcessing"() { println "Entering the test putProcessing" given: "a valid auth" Auth auth = Fixture.from(Auth.class).gimme("VALID") and: "a valid tokenParser" TokenParser jwtParser = Mock(TokenParser) and : "a mocked claims " Claims claims = Mock(Claims) claims.get("codigoFuncional", String.class) >> "1234-56" jwtParser.getBody("valid.jwt.token") >> claims and: "a mocked CacheApiRequest " CacheApiRequest cacheApiRequestMock = new CacheApiRequest("true", 1L) cacheMapper.map("true", 1L) >> cacheApiRequestMock and: "WebClient mocks are properly stubbed and verified" WebClient.RequestBodyUriSpec bodySpec = Mock(WebClient.RequestBodyUriSpec) WebClient.RequestHeadersSpec headerSpec = Mock(WebClient.RequestHeadersSpec) WebClient.ResponseSpec responseSpec = Mock(WebClient.ResponseSpec) // 合并存根与调用次数验证,确保每个链式调用都被正确匹配 1 * webClientMock.put() >> bodySpec 1 * bodySpec.uri(_ as String, _ as Function) >> bodySpec 2 * bodySpec.header(_ as String, _ as String) >> bodySpec 1 * bodySpec.contentType(MediaType.APPLICATION_JSON) >> bodySpec 1 * bodySpec.body(_ as Mono, CacheApiRequest.class) >> headerSpec 1 * headerSpec.retrieve() >> responseSpec 1 * responseSpec.bodyToMono(Void.class) >> Mono.empty() when: "call putProcessing" println "Starting putProcessing" repository.putProcessing(auth, jwtParser, "true") then: "all expected interactions are satisfied" // 此处无需重复验证webClient.put(),因为已在given块中合并声明 println "test completed" }
额外注意事项
- 如果
AuthUtils.getFuncional是静态方法,需要添加静态Mock:given: "mocked static utils" Mockito.mockStatic(AuthUtils.class) AuthUtils.getFuncional(auth, jwtParser) >> "1234-56" - 同理,
Utils.getFuncionalToogle也需要静态Mock:Mockito.mockStatic(Utils.class) Utils.getFuncionalToogle(_ as String) >> "1234-56"
内容的提问来源于stack exchange,提问作者oitathi
相关产品推荐
相关产品推荐

