当前位置: 首页 > 知识库问答 >
问题:

为什么webflux的spring boot测试会忽略自定义jackson模块

陈功
2023-03-14

我正在使用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实例,但也没有使用。我想知道我是否错过了一个设置(虽然我尝试了很多注释…)或者如果我碰到了虫子。我在谷歌上做了很多搜索,但迄今为止,我找到的任何解决方案都没有帮助。此外,目前很少有ppl使用路由器功能:)。

感谢您的帮助!

更新:我尝试了2.0.2。版本和2.1.0。以及BUILD-20180509。结果总是一样的。

共有2个答案

孔阳炎
2023-03-14

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

然而,删除@SpringBootTest会让我再次没有自定义序列化。所以我玩了一会儿@AutoCongraph...注释,找到了一个集合,允许我在嘲笑/省略其他所有内容的同时测试路由和序列化。

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
}
齐迪
2023-03-14

为了正确考虑Jackson模块,您可以使用AutoConfigureWebTestClient,并按以下方式自动连接它,而不是在测试中手动创建WebTestClient:

@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\"}");
    }
}
 类似资料:
  • 我正在尝试编写一个单元测试到一个自定义反序列化器,该反序列化器是使用一个带有@了的构造函数来实例化的,并且我的实体标有@JsonDesri的。它在我的集成测试中工作得很好,其中MockMvc会带来Spring serverside。 然而,在调用objectMapper.read值(…)的测试中,使用不带参数的默认构造函数的反序列化器的新实例被实例化。即使 实例化有线版本的反序列化程序,实际调用仍

  • 因此,我实现了一个自定义SerDe,它从Confluent提供的扩展到每当与模式注册表通信超时时,都会尝试重试。我已将Spring Cloud Streams Kafka binders配置为默认使用: 今天我在日志中看到了这个错误: 这告诉我Kafka Streams使用的SerDe不是我上面定义的SerDe,而是基类SpecificAvroSerde(它包装SpecificAvroSerial

  • 问题内容: 我正在尝试在包级别使用Hibernate @TypeDef批注,这与Hibernate文档中所描述的完全相同。我正在使用和。代码可以编译,并且在类路径中,但是Hibernate仍然看不到它。 如果我上课,那是行得通的,但是如果我把放在那,那是没有用的。我试图用Google搜索,但找不到任何有用的信息。 谢谢! 问题答案: 您可能需要添加一个 到您的Hibernate配置文件,或调用co

  • 我正在使用JPA2.1(由Hibernate4.2.11支持)和Spring4.0.2开发一个应用程序。我们正在使用Envers审核项目实体中的更改。这很好。当我们尝试使用自定义修订实体时,问题就出现了,正如Envers文档所述:http://docs.jboss.org/hibernate/core/4.1/devguide/en-US/html/ch15.html#envers-修正日志 正如

  • 问题内容: 我有一个带有关系的实体,我想通过一个查询来检索它,因此使用。有时,Hibernate不尊重它,而是发出N + 1 秒。随着 有时 我的意思是,因为我不知道是什么触发它,我有案件对不同的查询,这可能发生,或者不一样的类。 这是带有我使用的注释的简化实体: 用 我希望单个查询能够同时获取其及其内容,例如 相反,我得到了第一选择所有N S和那么N 献给所有S(考虑没有缓存)。 我发现了许多类