Почему весенний тест загрузки с webflux игнорирует пользовательский модуль Джексона - PullRequest
0 голосов
/ 10 мая 2018

Я пишу приложение с использованием функций Spring Boot 2.0.1 и маршрутизатора WebFlux ( не на основе аннотаций!). Для некоторых моих объектов данных я написал собственные сериализаторы, которые расширяют StdSerializer. Я регистрирую их в SimpleModule и выставляю этот модуль как компонент.

Эта настройка работает как чудо, когда я запускаю приложение. Компонент создается, а ответы 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();
    }
}

Сериализатор обернут в модуль Джексона и выставлен как боб. Этот компонент правильно выбран и добавлен в ObjectMapper при запуске самого приложения.

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

        return serializerModule;
    }
}

Я также подтвердил, что бин действительно присутствует в тесте. Поэтому я могу исключить, что контекст, созданный во время тестирования, отсутствует для загрузки 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. Результат всегда один и тот же.

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

В то время как решение, представленное Себастьеном, работало безупречно в демо-коде, у меня были некоторые проблемы после введения его в основное приложение. @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
}
0 голосов
/ 11 мая 2018

Вместо того, чтобы создавать WebTestClient вручную в тесте, вы, вероятно, могли бы использовать @AutoConfigureWebTestClient и автоматически подключить его следующим образом, чтобы правильно принять во внимание ваш модуль Джексона:

@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\"}");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...