Какой рекомендуемый способ запустить тест весенней загрузки, когда только один тестируемый объект настроен в контексте.
Если я аннотирую тест
@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).




Определенно разумно и нормально тестировать только один класс в модульном тесте.
Нет проблем с включением только одного bean-компонента в ваш тестовый контекст. На самом деле, @Configuration (обычно) представляет собой просто набор компонентов. Вы могли бы гипотетически создать класс @Configuration только с MyTestBean, но в этом действительно не было бы необходимости, поскольку вы можете сделать то же самое, перечислив свои контекстные bean-компоненты с @ContextConfiguration#classes.
Однако я хочу отметить, что для тестирования только одного bean-компонента в истинном модульном тесте передовая практика в идеале склоняется к настройке bean-компонента через конструктор и тестированию класса таким образом. Это ключевая причина, по которой парни из Spring рекомендуют использовать конструктор вместо инъекции свойств. См. Раздел под названием DI на основе конструктора или установщикаэтой статьи, Комментарий Оливера Гирке (т.е. глава проекта Spring Data) и Google для получения дополнительной информации. Вероятно, это причина того, что у вас возникает странное чувство при настройке контекста для одного bean-компонента!
Спасибо за ответ, причина, по которой я настраивал контекст в моем случае с @SpringBootTest(properties = "spring.profiles.active=test"), заключается в том, что класс application-test.properties настраивает некоторые аспекты контекста (такие как уровни журналов). Изначально я начал, не будучи тестом контекста Spring - это был простой тест junit, который только что создал новый экземпляр POJO - но на самом деле я хочу, чтобы конфигурация применялась в соответствии со свойствами теста.
@Dovmo Я проголосовал за, но я еще не принял ответ, так как он хорошо работает или усиливает некоторые принципы хороших модульных тестов и дает хорошие советы по лучшим практикам DI, но на самом деле не учитывает, был ли подход, который я take - допустимый способ использования аннотаций w spring для управления контекстом, вместо этого он предлагает переключиться на чистые модульные тесты без контекста Spring и рекомендуется внедрение конструктора.
Обновлено по вашему комментарию! Я должен был изначально подчеркнуть, что ваш первоначальный подход - правильный подход; Мне однажды приходилось проводить модульное тестирование клиентского кода Spring, и я думаю, что настройка этих конкретных bean-компонентов через @CC - это четкий и лаконичный способ сделать это!
Для начала рекомендуется сначала прочитать документацию (например, JavaDoc, ссылка на который приведена ниже в этом ответе), поскольку она уже отвечает на ваш вопрос.
If I understand correctly the class that I reference is supposed to be a
Configurationclass, 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.injectannotations- 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 или выше.
Продолжение выше: я не хочу использовать файл журнала для всего набора тестов, я просто хочу, чтобы этот тест выполнялся с «ожидаемым» уровнем. Я добился этого, как показано выше, аннотировав тест как тест Spring и позволив spring настраивать контекст на время теста на основе содержимого моего файла application-test.properties, который, как правило, устанавливает logging.level.com.example=DEBUG. Я вижу, что "необычно" тестировать один компонент таким образом, учитывая, что мне вообще не нужен контекст Spring - я использую побочный эффект наличия контекста (а именно, что установлен уровень журнала)
Вы также можете использовать 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();
}
}
}
В дополнение к прекрасному предыдущему объяснению, если вы не использовали конструкторы в своих bean-компонентах, вы можете легко установить необходимое поле с помощью
ReflectionTestUtils.setFieldиз пакетаorg.springframework.test.util.