为啥使用 webflux 进行 spring boot 测试会忽略自定义 jackson 模块

     2023-02-27     81

关键词:

【中文标题】为啥使用 webflux 进行 spring boot 测试会忽略自定义 jackson 模块【英文标题】:Why does spring boot test with webflux ignore custom jackson module为什么使用 webflux 进行 spring boot 测试会忽略自定义 jackson 模块 【发布时间】:2018-10-20 01:54:40 【问题描述】:

我正在使用 Spring Boot 2.0.1 和 WebFlux 路由器功能(基于注释!)编写应用程序。对于我的一些数据对象,我编写了扩展StdSerializer 的自定义序列化程序。这些我在 SimpleModule 中注册并将该模块公开为 bean。

当我运行应用程序时,这个设置就像一个魅力。 bean 被实例化,REST 响应使用正确的序列化程序进行序列化。

现在我想编写一个测试来验证路由器功能和它们背后的处理程序是否按预期工作。我想模拟的处理程序背后的服务。但是,在测试中,REST 响应使用默认序列化程序

我创建了一个重现该问题的小型演示项目。完整代码可以在这里找到:http://s000.tinyupload.com/?file_id=82815835861287011625

Gradle 配置加载 Spring Boot 和一些依赖项以支持 WebFlux 和测试。

import io.spring.gradle.dependencymanagement.DependencyManagementPlugin
import org.springframework.boot.gradle.plugin.SpringBootPlugin

buildscript 
    ext 
        springBootVersion = '2.0.1.RELEASE'
    
    repositories 
        mavenCentral()
        // To allow to pull in milestone releases from Spring
        maven  url 'https://repo.spring.io/milestone' 
    
    dependencies 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
        classpath("io.spring.gradle:dependency-management-plugin:1.0.5.RELEASE")

    


apply plugin: 'java'
apply plugin: SpringBootPlugin
apply plugin: DependencyManagementPlugin


repositories 
    mavenCentral()

    // To allow to pull in milestone releases from Spring
    maven  url 'https://repo.spring.io/milestone' 


dependencyManagement 
    imports 
        mavenBom 'org.springframework.boot:spring-boot-dependencies:2.0.1.RELEASE'
    


dependencies 
    compile 'org.springframework.boot:spring-boot-starter-webflux'

    compile 'org.slf4s:slf4s-api_2.12:1.7.25'

    testCompile 'org.springframework.boot:spring-boot-starter-test'
    testCompile 'org.springframework.boot:spring-boot-starter-json'
    testCompile 'junit:junit:4.12'
    testCompile "org.mockito:mockito-core:2.+"

数据对象有两个字段。

package com.example.model;

public class ReverserResult 
    private String originalString;
    private String reversedString;

    // ... constructor, getters

自定义序列化程序以与默认序列化程序完全不同的方式呈现数据对象。原来的字段名消失,数据对象的内容被压缩成一个字符串。

@Component
public class ReverserResultSerializer extends StdSerializer<ReverserResult> 
    // ... Constructor ...

    @Override
    public void serialize(ReverserResult value, JsonGenerator gen, SerializerProvider provider) throws IOException 
        gen.writeStartObject();
        gen.writeFieldName("result");
        gen.writeString(value.getOriginalString() + "|" + value.getReversedString());
        gen.writeEndObject();
    

序列化器封装在 Jackson 模块中并作为 bean 公开。在运行实际应用程序时,该 bean 被正确拾取并添加到 ObjectMapper

@Configuration
public class SerializerConfig 
    @Bean
    @Autowired public Module specificSerializers(ReverserResultSerializer reverserResultSerializer) 
        SimpleModule serializerModule = new SimpleModule();
        serializerModule.addSerializer(ReverserResult.class, reverserResultSerializer);

        return serializerModule;
    

我还验证了 bean 确实存在于测试中。所以我可以排除测试期间创建的上下文缺少加载 bean。

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReverserRouteTest 
    @Autowired
    public ReverserRoutes reverserRoutes;

    @MockBean
    public ReverserService mockReverserService;

    @Autowired
    @Qualifier("specificSerializers")
    public Module jacksonModule;

    @Test
    public void testSerializerBeanIsPresent() 
        assertNotNull(jacksonModule);
    

    @Test
    public void testRouteAcceptsCall() 
        given(mockReverserService.reverse(anyString())).willReturn(new ReverserResult("foo", "bar"));

        WebTestClient client = WebTestClient.bindToRouterFunction(reverserRoutes.createRouterFunction()).build();
        client.get().uri("/reverse/FooBar").exchange().expectStatus().isOk();
    

    @Test
    public void testRouteReturnsMockedResult() 
        given(mockReverserService.reverse(anyString())).willReturn(new ReverserResult("foo", "bar"));

        WebTestClient client = WebTestClient.bindToRouterFunction(reverserRoutes.createRouterFunction()).build();
        client.get().uri("/reverse/somethingcompletelydifferent")
                .exchange()
                .expectBody().json("\"result\":\"foo|bar\"");
    

运行应用时的结果:

GET http://localhost:9090/reverse/FooBar

HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json;charset=UTF-8


  "result": "FooBar|raBooF"

运行测试时的结果:

< 200 OK
< Content-Type: [application/json;charset=UTF-8]

"originalString":"foo","reversedString":"bar"

我也尝试创建自己的 ObjectMapper 实例,但也没有使用。我想知道我是否缺少设置(尽管我确实尝试了很多注释......)或者我是否遇到了错误。我在 Google 和 SO 上进行了很多搜索,但到目前为止我找到的解决方案都没有帮助。此外,到目前为止,很少有人使用路由器功能:)。

感谢任何帮助!

更新:我也尝试了 2.0.2.RELEASE 和 2.1.0.BUILD-20180509。结果总是一样的。

【问题讨论】:

【参考方案1】:

除了在测试中手动创建 WebTestClient 之外,您还可以利用@AutoConfigureWebTestClient 并按以下方式自动连接它,以便正确考虑您的 Jackson 模块:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWebTestClient
public class ReverserRouteTest     
    @MockBean
    public ReverserService mockReverserService;

    @Autowired
    @Qualifier("specificSerializers")
    public Module jacksonModule;

    @Autowired
    public WebTestClient client;

    @Test
    public void testSerializerBeanIsPresent() 
        assertNotNull(jacksonModule);
    

    @Test
    public void testRouteAcceptsCall() 
        given(mockReverserService.reverse(anyString())).willReturn(new ReverserResult("foo", "bar"));

        client.get().uri("/reverse/FooBar").exchange().expectStatus().isOk();
    

    @Test
    public void testRouteReturnsMockedResult() 
        given(mockReverserService.reverse(anyString())).willReturn(new ReverserResult("foo", "bar"));

        client.get().uri("/reverse/somethingcompletelydifferent")
                .exchange()
                .expectBody().json("\"result\":\"foo|bar\"");
    

【讨论】:

完美!这解决了问题!非常感谢!我确实尝试过使用 \@WebFluxTest,但我从未想过将 \@SpringBootTest 和 \@AutoConfigureWebTestClient 结合起来。我已经在 GitHub 上发布了工作代码,以防有人想以它为例:github.com/DerEros/demo-webflux-test-issue/tree/…【参考方案2】:

虽然 Sébastien 提出的解决方案在演示代码中完美运行,但在将其引入主应用程序后我遇到了一些问题。 @SpringBootTest 会引入太多的 bean,这反过来又需要大量的外部配置设置等。并且测试应该只涵盖路由和序列化。

然而,删除 @SpringBootTest 会让我再次没有自定义序列化。所以我对@AutoConfigure... 注释进行了一些尝试,并找到了一个允许我在模拟/省略其他所有内容的同时测试路由和序列化的集合。

完整代码可在 GitHub https://github.com/DerEros/demo-webflux-test-issue/tree/with-webfluxtest 上获得。

相关的变化在这里。希望这对其他人也有帮助。

@RunWith(SpringRunner.class)
@WebFluxTest
@AutoConfigureWebClient
@Import(SerializerConfig.class, ReverserResultSerializer.class, ReverserRoutes.class, ReverseHandler.class, ReverserConfig.class)
public class ReverserRouteTest 
    @MockBean
    public ReverserService mockReverserService;

    @Autowired
    @Qualifier("specificSerializers")
    public Module jacksonModule;

    @Autowired
    public WebTestClient client;

    // Tests; no changes here

【讨论】:

如何使用 Spring Boot 对 WebFlux 进行异常处理?

】如何使用SpringBoot对WebFlux进行异常处理?【英文标题】:HowtodoExceptionHandlingforWebFluxusingSpringboot?【发布时间】:2020-01-2011:50:23【问题描述】:我有3个微服务应用程序。我正在尝试使用响应包中的webclient进行2获取异步调用,然后... 查看详情

Spring Webflux:如何使用不同的线程进行请求和响应

】SpringWebflux:如何使用不同的线程进行请求和响应【英文标题】:SpringWebflux:Howtousedifferentthreadforrequestandresponse【发布时间】:2019-12-0106:37:24【问题描述】:我正在使用SpringWebflux,据我了解,通过使用它,用于接收请求的线程和... 查看详情

无法使用 Spring Webflux 和 Thymeleaf 对静态资源进行版本控制

】无法使用SpringWebflux和Thymeleaf对静态资源进行版本控制【英文标题】:NotabletoversionstaticresourceswithSpringWebfluxandThymeleaf【发布时间】:2019-05-1600:01:18【问题描述】:我尝试使用SpringWebflux在我的应用程序上实现静态内容版本控制,... 查看详情

为啥spring webflux默认选择jetty然后失败?

】为啥springwebflux默认选择jetty然后失败?【英文标题】:Whyspringwebfluxischoosingjettybydefaultandthenfailing?为什么springwebflux默认选择jetty然后失败?【发布时间】:2018-05-2413:38:50【问题描述】:我正在尝试运行基于Springboot2.0.0.M7的应用... 查看详情

为啥 Spring Webflux 只能并行接受 256 个请求?

】为啥SpringWebflux只能并行接受256个请求?【英文标题】:WhydoesSpringWebfluxonlyaccepts256requestsinparallel?为什么SpringWebflux只能并行接受256个请求?【发布时间】:2019-03-2712:00:54【问题描述】:在默认配置中,SpringWebflux似乎将并行请求... 查看详情

为啥默认配置的spring webflux中没有异常堆栈跟踪?

】为啥默认配置的springwebflux中没有异常堆栈跟踪?【英文标题】:Whythereisnoexceptionstacktraceinspringwebfluxwithdefaultconfiguration?为什么默认配置的springwebflux中没有异常堆栈跟踪?【发布时间】:2019-05-1713:33:18【问题描述】:问题我在We... 查看详情

为啥 Spring WebFlux 应用程序不支持 @RestController 映射前缀?

】为啥SpringWebFlux应用程序不支持@RestController映射前缀?【英文标题】:Whydoesn\'taSpringWebFluxapphonorthe@RestControllermappingprefiix?为什么SpringWebFlux应用程序不支持@RestController映射前缀?【发布时间】:2020-02-2307:31:49【问题描述】:我在... 查看详情

spring boot webflux中如何根据条件进行两次删除操作?

】springbootwebflux中如何根据条件进行两次删除操作?【英文标题】:Howtodotwodeleteoperationbasedonconditioninspringbootwebflux?【发布时间】:2021-08-1812:13:17【问题描述】:我正在尝试根据以下条件使用Webflux进行删除操作(r2dbc),但switchIfe... 查看详情

为啥在 webflux 中使用 WebFilter corsFilter 时请求返回 404 状态?

】为啥在webflux中使用WebFiltercorsFilter时请求返回404状态?【英文标题】:Whyrequestsarereturned404statuswhenusingWebFiltercorsFilterinwebflux?为什么在webflux中使用WebFiltercorsFilter时请求返回404状态?【发布时间】:2019-11-2315:33:52【问题描述】:... 查看详情

spring-webflux 中处理错误的正确方法是啥

】spring-webflux中处理错误的正确方法是啥【英文标题】:whatistherightwaytohandleerrorsinspring-webfluxspring-webflux中处理错误的正确方法是什么【发布时间】:2017-09-2009:27:32【问题描述】:我一直在使用spring-webflux进行一些研究,我想了解... 查看详情

Spring Webflux WebClient

】SpringWebfluxWebClient【英文标题】:【发布时间】:2018-05-0901:13:32【问题描述】:我真的不知道如何正确地将以下调用转换为springwebfluxwebclient。userIds是列表,我可以使用以下语法调用该服务,但我无法使用SpringWebFluxWebClient进行... 查看详情

spring-webflux使用,一文带你从0开始学明白spring-webflux,学明白响应式编程

文章目录​​一、Spring-WebFlux介绍​​​​区别于SpringMVC​​​​二、Spring-WebFlux的使用​​​​1、注解编程模型​​​​(1)定义实体类​​​​(2)定义service​​​​(3)定义controller​​​​(4)测试一下吧~​​​​2... 查看详情

在 Spring Webflux 中执行阻塞 JDBC 调用

】在SpringWebflux中执行阻塞JDBC调用【英文标题】:ExecuteblockingJDBCcallinSpringWebflux【发布时间】:2019-07-1817:34:17【问题描述】:我正在使用SpringWebflux和Springdatajpa,使用PostgreSql作为后端数据库。我不想在进行find和save之类的数据库调... 查看详情

在 Spring WebFlux webclient 中设置超时

】在SpringWebFluxwebclient中设置超时【英文标题】:settimeoutinSpringWebFluxwebclient【发布时间】:2018-08-0604:11:07【问题描述】:我正在使用SpringWebfluxWebClient从我的Spring启动应用程序进行REST调用。并且每次都会在30秒内超时。这是我尝试... 查看详情

Spring WebFlux、安全性和请求体

】SpringWebFlux、安全性和请求体【英文标题】:SpringWebFlux,Securityandrequestbody【发布时间】:2021-06-2314:25:42【问题描述】:我需要使用请求正文的HMAC来保护通过SpringBoot、WebFlux和springsecurity实现的RESTAPI。稍微简化一下,在高层次上-... 查看详情

为啥 WebFlux-WebClient 超时不起作用?

】为啥WebFlux-WebClient超时不起作用?【英文标题】:WhyWebFlux-WebClientTimeoutnotworking?为什么WebFlux-WebClient超时不起作用?【发布时间】:2021-07-0702:07:26【问题描述】:我正在使用SpringbootWebflux2.4.4(最新)并尝试使用WebClient调用后端UR... 查看详情

Spring @Async 与 Spring WebFlux

】Spring@Async与SpringWebFlux【英文标题】:Spring@AsyncvsSpringWebFlux【发布时间】:2021-02-1916:21:41【问题描述】:我正在尝试使用WebFlux转换以下方法以避免在同一方法上使用@Async。@Async@OverridepublicvoidnotifyCallback(NotifyCallbackRequestrequest,Stri... 查看详情

服务器使用 Spring Boot 和 WebFlux 发送事件

】服务器使用SpringBoot和WebFlux发送事件【英文标题】:ServerSentEventswithSpringBootandWebFlux【发布时间】:2019-01-2612:31:08【问题描述】:我正在处理服务器发送事件。我得到了帮助http://sinhamohit.com/writing/spring-boot-reactive-ssehttps://github.com... 查看详情