Тест контекста Spring с помощью всего одного компонента

Какой рекомендуемый способ запустить тест весенней загрузки, когда только один тестируемый объект настроен в контексте.

Если я аннотирую тест

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
@ContextConfiguration(classes = MyTestBean.class)

Тогда кажется, что это работает - тест проходит, контекст запускается быстро и, кажется, содержит только тот bean-компонент, который мне нужен. Однако это похоже на неправильное использование аннотации @ContextConfiguration(classes = MyTestBean.class). Если я правильно понимаю, класс, на который я ссылаюсь, должен быть классом Configuration, а не, например, обычным компонентом или компонентом службы Spring.

Это правильно? Или это действительно действенный способ достичь поставленной цели? Я знаю, что есть более сложные примеры, такие как org.springframework.boot.test.autoconfigure.json.JsonTest, которые используют @TypeExcludeFilters(JsonExcludeFilter.class) для управления контекстом, но это кажется излишним для моего варианта использования. Мне просто нужен контекст для моего единственного bean-компонента.

Разъяснение

Я знаю, что могу просто создать один bean-компонент, который я тестирую как POJO, без проверки контекста Spring и удалить три приведенные выше аннотации. Но в моем конкретном случае использования я на самом деле полагаюсь на некоторую конфигурацию, применяемую к контексту с помощью настроек в файле application-test.properties, поэтому я сделал это тестом Spring Boot с набором профилей. С моей точки зрения, это не простой модульный тест одного класса в изоляции от конфигурации контекста Spring - тест зависит от применяемой определенной конфигурации (которая в настоящее время предоставляется свойствами приложения загрузки Spring). Я действительно могу просто протестировать компоненты как POJO, создав новый экземпляр вне контекста Spring, я использую инъекцию конструктора, что упрощает предоставление необходимых зависимостей, но тест полагается на такие вещи, как уровень журнала (тест фактически делает утверждения для определенных создаваемых журналов), который требует, чтобы уровень журнала был установлен правильно (что в настоящее время выполняется через logging.level.com.example=DEBUG в файле свойств, который устанавливает контекст Spring).

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
9
0
6 534
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Определенно разумно и нормально тестировать только один класс в модульном тесте.

Нет проблем с включением только одного bean-компонента в ваш тестовый контекст. На самом деле, @Configuration (обычно) представляет собой просто набор компонентов. Вы могли бы гипотетически создать класс @Configuration только с MyTestBean, но в этом действительно не было бы необходимости, поскольку вы можете сделать то же самое, перечислив свои контекстные bean-компоненты с @ContextConfiguration#classes.

Однако я хочу отметить, что для тестирования только одного bean-компонента в истинном модульном тесте передовая практика в идеале склоняется к настройке bean-компонента через конструктор и тестированию класса таким образом. Это ключевая причина, по которой парни из Spring рекомендуют использовать конструктор вместо инъекции свойств. См. Раздел под названием DI на основе конструктора или установщикаэтой статьи, Комментарий Оливера Гирке (т.е. глава проекта Spring Data) и Google для получения дополнительной информации. Вероятно, это причина того, что у вас возникает странное чувство при настройке контекста для одного bean-компонента!

В дополнение к прекрасному предыдущему объяснению, если вы не использовали конструкторы в своих bean-компонентах, вы можете легко установить необходимое поле с помощью ReflectionTestUtils.setField из пакета org.springframework.test.util.

borino 27.06.2018 07:43

Спасибо за ответ, причина, по которой я настраивал контекст в моем случае с @SpringBootTest(properties = "spring.profiles.active=test"), заключается в том, что класс application-test.properties настраивает некоторые аспекты контекста (такие как уровни журналов). Изначально я начал, не будучи тестом контекста Spring - это был простой тест junit, который только что создал новый экземпляр POJO - но на самом деле я хочу, чтобы конфигурация применялась в соответствии со свойствами теста.

David 27.06.2018 11:50

@Dovmo Я проголосовал за, но я еще не принял ответ, так как он хорошо работает или усиливает некоторые принципы хороших модульных тестов и дает хорошие советы по лучшим практикам DI, но на самом деле не учитывает, был ли подход, который я take - допустимый способ использования аннотаций w spring для управления контекстом, вместо этого он предлагает переключиться на чистые модульные тесты без контекста Spring и рекомендуется внедрение конструктора.

David 27.06.2018 14:10

Обновлено по вашему комментарию! Я должен был изначально подчеркнуть, что ваш первоначальный подход - правильный подход; Мне однажды приходилось проводить модульное тестирование клиентского кода Spring, и я думаю, что настройка этих конкретных bean-компонентов через @CC - это четкий и лаконичный способ сделать это!

Dovmo 28.06.2018 05:41
Ответ принят как подходящий

Для начала рекомендуется сначала прочитать документацию (например, JavaDoc, ссылка на который приведена ниже в этом ответе), поскольку она уже отвечает на ваш вопрос.

If I understand correctly the class that I reference is supposed to be a Configuration class, not a regular spring service bean or component for example.

Is that right?

Нет, это не совсем так.

Классы, предоставленные для @ContextConfiguration, являются классами обычно@Configuration, но это не обязательно.

Вот отрывок из JavaDoc для @ContextConfiguration:

Annotated Classes

The term annotated class can refer to any of the following.

  • A class annotated with @Configuration
  • A component (i.e., a class annotated with @Component, @Service, @Repository, etc.)
  • A JSR-330 compliant class that is annotated with javax.inject annotations
  • Any other class that contains @Bean-methods

Таким образом, вы можете передать @ContextConfiguration любой «аннотированный класс».

Or is this indeed a valid way to achieve this goal?

Фактически, это действенный способ достижения этой цели; однако также немного необычно загружать ApplicationContext, содержащий одиночный пользовательский компонент.

С уважением,

Сэм (автор Spring TestContext Framework)

Спасибо за обратную связь - очень полезную и информативную, особенно из авторитетного источника, такого как вы. Мне бы очень хотелось услышать ваше мнение о моем конкретном варианте использования - чтобы дать вам полный контекст по этому поводу: у меня есть компонент, который использует SLF4J для регистрации различных операторов. У меня есть тест, который хочет подтвердить, что журналы написаны правильно (переменные и формат соответствуют ожидаемым). Для этого в тесте используется правило JUnit OutputCaputre. Компонент использует уровень DEBUG. Чтобы мой тест работал надежно, я должен установить уровень журнала на DEBUG или выше.

David 27.06.2018 17:34

Продолжение выше: я не хочу использовать файл журнала для всего набора тестов, я просто хочу, чтобы этот тест выполнялся с «ожидаемым» уровнем. Я добился этого, как показано выше, аннотировав тест как тест Spring и позволив spring настраивать контекст на время теста на основе содержимого моего файла application-test.properties, который, как правило, устанавливает logging.level.com.example=DEBUG. Я вижу, что "необычно" тестировать один компонент таким образом, учитывая, что мне вообще не нужен контекст Spring - я использую побочный эффект наличия контекста (а именно, что установлен уровень журнала)

David 27.06.2018 17:36

Вы также можете использовать ApplicationContextRunner для создания вашего контекста, используя тестовую конфигурацию по вашему выбору (даже с одним bean-компонентом, если хотите, но, как другие люди уже упоминали для одного bean-компонента, более разумно использовать конструктор классическим способом без использования какой-либо магии Spring).

Что мне нравится в этом способе тестирования, так это то, что тест выполняется очень быстро, поскольку вы не загружаете весь контекст. Этот метод лучше всего использовать, когда тестируемый компонент не имеет зависимостей Autowired, в противном случае удобнее использовать @SpringBootTest.

Ниже приведен пример, показывающий, как вы можете использовать его для достижения своей цели:

class MyTest {

  @Test
  void test_configuration_should_contains_my_bean() {

    new ApplicationContextRunner()
        .withUserConfiguration(TestConfiguration.class)
        .run(context -> {
          assertThat(context.getBean(MyTestBean.class)).isNotNull();
        });
  }

  @Configuraiton
  public static class TestConfiguration {
    @Bean
    public MyTestBean myTestBean(){
        new MyTestBean();
    }
  }
}

Другие вопросы по теме