spring - 测试 spring 应用程序上下文无法启动的最佳方法是什么?

标签 spring junit spring-boot spring-test

我使用 spring-boot-starter-web 和 spring-boot-starter-test。

假设我有一个用于绑定(bind)配置属性的类:

@ConfigurationProperties(prefix = "dummy")
public class DummyProperties {

    @URL
    private String url;

    // getter, setter ...

}

现在我想测试我的 bean 验证是否正确。如果属性 dummy.value 未设置或包含无效 URL,则上下文应该无法启动(带有特定错误消息)。如果属性包含有效的 URL,则上下文应该开始。 (测试将显示 @NotNull 丢失。)

一个测试类看起来像这样:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@IntegrationTest({ "dummy.url=123:456" })
public class InvalidUrlTest {
    // my test code
}

此测试将失败,因为提供的属性无效。告诉 Spring/JUnit 的最佳方式是什么:“是的,这个错误是意料之中的”。在普通的 JUnit 测试中,我会使用 ExpectedException。

最佳答案

测试 Spring 应用程序上下文的最佳方法是使用 ApplicationContextRunner它在 Spring Boot 引用文档中有描述:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html#boot-features-test-autoconfig
有一个关于它的快速指南:
https://www.baeldung.com/spring-boot-context-runner
示例用法

private static final String POSITIVE_CASE_CONFIG_FILE =  
"classpath:some/path/positive-case-config.yml";
private static final String NEGATIVE_CASE_CONFIG_FILE =  
"classpath:some/path/negative-case-config.yml";

@Test
void positiveTest() {
  ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withInitializer(new ConfigDataApplicationContextInitializer())//1
    .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
    .withUserConfiguration(MockBeansTestConfiguration.class)//3
    .withPropertyValues("spring.config.location=" + POSITIVE_CASE_CONFIG_FILE)//4
    .withConfiguration(AutoConfigurations.of(BookService.class));//5
  contextRunner
    .run((context) -> {
      Assertions.assertThat(context).hasNotFailed();//6
    });
}

@Test
void negativeTest() {
  ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withInitializer(new ConfigDataApplicationContextInitializer())//1
    .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
    .withUserConfiguration(MockBeansTestConfiguration.class)//3
    .withPropertyValues("spring.config.location=" + NEGATIVE_CASE_CONFIG_FILE)//4
    .withConfiguration(AutoConfigurations.of(BookService.class));//5
  contextRunner
    .run((context) -> {
      assertThat(context)
        .hasFailed();
      assertThat(context.getStartupFailure())
        .isNotNull();
      assertThat(context.getStartupFailure().getMessage())
        .contains("Some exception message");
      assertThat(extractFailureCauseMessages(context))
        .contains("Cause exception message");
    });
}

private List<String> extractFailureCauseMessages(AssertableApplicationContext context) {
  var failureCauseMessages = new ArrayList<String>();
  var currentCause = context.getStartupFailure().getCause();
  while (!Objects.isNull(currentCause)) {//7
    failureCauseMessages.add(currentCause.getMessage());
    currentCause = currentCause.getCause();
  }
  return failureCauseMessages;
}
Junit5 Spring Boot Test Annotations 中类似定义的示例说明:
  • 触发加载配置文件,如 application.propertiesapplication.yml
  • 日志 ConditionEvaluationReport当应用程序上下文失败时使用给定的日志级别
  • 提供指定模拟 bean 的类,即。我们有 @Autowired BookRepository在我们的 BookService我们提供模拟 BookRepositoryMockBeansTestConfiguration .类似于 @Import({MockBeansTestConfiguration.class})在测试课和 @TestConfiguration在普通 Junit5 Spring Boot 测试中使用模拟 bean 上课
  • 等效于 @TestPropertySource(properties = { "spring.config.location=" + POSITIVE_CASE_CONFIG_FILE})
  • 触发给定类的 spring 自动配置,不是直接等效的,但类似于使用 @ContextConfiguration(classes = {BookService.class})@SpringBootTest(classes = {BookService.class})连同@Import({BookService.class})正常测试中
  • AssertJ 库中的 Assertions.class,应该有 Assertions.assertThat 的静态导入,但我想说明此方法的来源
  • Objects.isNull 应该有静态导入,但我想说明此方法的来源

  • MockBeansTestConfiguration 类:
    @TestConfiguration
    public class MockBeansTestConfiguration {
      private static final Book SAMPLE_BOOK = Book.of(1L, "Stanisław Lem", "Solaris", "978-3-16-148410-0");
    
      @Bean
      public BookRepository mockBookRepository() {
        var bookRepository = Mockito.mock(BookRepository.class);//1
        Mockito.when(bookRepository.findByIsbn(SAMPLE_BOOK.getIsbn()))//2
               .thenReturn(SAMPLE_BOOK);
        return bookRepository;
      }
    }
    
    评论:
    1,2。应该有静态导入,但我想展示这个方法的来源

    关于spring - 测试 spring 应用程序上下文无法启动的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31692863/

    相关文章:

    java - Spring Boot 和 Thymeleaf : Switching Pages through Nav bar links

    java - 如何用Java编写SQL查询从Snowflake获取数据

    android - 将 Spring 依赖注入(inject)代码移至 Android

    java - 如何编写一个junit测试来在操作之间插入双向链表?

    java - 如何在Spring Security中设置always-use-default-target?

    eclipse - 在 Eclipse 中哪里可以找到 JUNIT_CONTAINER 的值?

    gradle - CircleCI 是否能够在没有额外插件的情况下解释通过 Gradle 生成的 JUnit XML 结果?

    spring-boot - 在 Springboot 2.2.6 中找不到 jwt 依赖项

    java - 为什么通过主键进行简单的 Hibernate findOne() 需要这么长时间?

    java - Spring Boot Thymeleaf 下拉列表