使用 spock 进行单元测试 spring 云网关过滤器

Unit testing spring cloud gateway filters with spock

我正在使用响应式全局过滤器在网关响应中添加 cookie:

chain.filter(exchange).then(<a mono relevant to response>)

当我尝试使用 spock 对此进行测试时 然后 方法不会从存根的 Mono 中调用。

过滤器本身:

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    return chain.filter(exchange)
            .then(refreshCookiesMono(exchange));
}

private Mono<Void> refreshCookiesMono(ServerWebExchange exchange) {
    return Mono.fromRunnable(() -> {
        //interactions with *exchange* and *chain*
    });
}

尽管最后有 0 * _,但该测试通过了:

@Subject
CookieFilter cookieFilter = new CookieFilter(cookieHelper)
...
ServerWebExchange exchange = Mock ServerWebExchange
GatewayFilterChain chain = Mock GatewayFilterChain
Mono<Void> mono = Mock Mono

...

def "cookieFilter refreshes the cookie with a new value"() {
    given:

    when:
    cookieFilter.filter(exchange, chain)

    then:
    1 * chain.filter(exchange) >> mono
    0 * _
}

但在代码中,我从 .filter 方法返回的单声道调用 .then

为什么不考虑 mono.then()?当然,当我尝试测试所有底层逻辑时 - spock 找不到交互。

chain.filter(exchange) returns 您模拟的单声道实例。

您没有对该模拟指定任何期望(我相信这就是您问题的答案),因此测试并没有真正检查过滤器,它只检查是否有一个调用 chain.filter(exchange).

此外,除了 Mocks 之外,Spock 还支持 Stubs,与许多其他框架不同的是,它们之间存在差异:

模拟是 "heavier",您可以对它们进行验证(在 "then" 块中),存根要多得多 "lightweight",您通常可以在其中指定对它们的期望一个 "given" 块。 通常,如果您想模拟某些交互并将测试基于围绕该交互进行管理的协议,则通常使用 Mocks,在其他情况下,Stub 更可取。

失去端到端测试过滤器的希望我在一个单独的包私有方法中提取了我的可运行对象,并在没有 Monos 和任何其他反应性东西的情况下对其进行了测试。

我的过滤器中的代码现在如下所示:

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    return chain.filter(exchange)
            .then(Mono.fromRunnable(refreshCookies(exchange)));
}

Runnable refreshCookies(ServerWebExchange exchange) {
    return () -> {
        //magic happens here ....
    };
}

感谢任何进一步的线索和重构建议。