【问题标题】:Configuring Spring's @DataJpaTest that overrides default application context beans配置覆盖默认应用程序上下文 bean 的 Spring 的 @DataJpaTest
【发布时间】:2020-05-17 17:57:57
【问题描述】:

我正在为我的@DataJpaTest 进行配置。我想利用 @DataJpaTest 提供的自动配置的 spring 上下文,但我想覆盖它的一些 bean。

这是我的主要课程:

public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(BookInputPort bookInputPort) {
        return args -> {
            bookInputPort.addNewBook(new BookDto("ABC", "DEF"));
            bookInputPort.addNewBook(new BookDto("GHI", "JKL"));
            bookInputPort.addNewBook(new BookDto("MNO", "PRS"));
        };
    }

如您所见,我为 CommandLineRunner 提供了依赖于某些服务的实现。

我也有一个测试:

@DataJpaTest
public class BookRepositoryTest {

    public static final String TITLE = "For whom the bell tolls";
    public static final String AUTHOR = "Hemingway";

    @Autowired
    private BookRepository bookRepository;

    @Test
    public void testRepository() {
        Book save = bookRepository.save(new Book(TITLE, AUTHOR));
        assertEquals(TITLE, save.getTitle());
        assertEquals(AUTHOR, save.getAuthor());
    }
}

当我运行测试时,我收到以下错误:

No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

这完全有道理!自动配置的测试只为上下文的“切片”提供实现。显然缺少 BookInputPort 的实现。我在测试上下文中不需要这个 commandLineRunner。我创建了一个不依赖于任何服务的 commandLineRunner。 我可以尝试通过添加到我的测试类嵌套类来解决这个问题:

@TestConfiguration
    static class BookRepositoryTestConfiguration {

        @Bean
        CommandLineRunner commandLineRunner() {
            return args -> {
            };
        }
    }

这样就解决了问题。有点儿。如果我有更多这样的测试,我将不得不将此嵌套类复制粘贴到每个测试类。这不是最佳解决方案。 我试图将其外部化为可以由@Import 导入的配置 这是配置类:

@Configuration
public class MyTestConfiguration {

    @Bean
    public CommandLineRunner commandLineRunner() {
        return args -> {
        };
    }
}

但随后应用程序失败并显示一条消息:

Invalid bean definition with name 'commandLineRunner' defined in com.example.demo.DemoApplication: Cannot register bean definition

我检查了这个错误和其他人在这种情况下的建议:

@DataJpaTest(properties = "spring.main.allow-bean-definition-overriding=true")

我做到了,我得到了:

No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

这与我一开始的问题完全相同。 我采取了所有这些步骤,发现自己回到了最初的位置。

你知道如何解决这个问题吗?我没有模糊的想法或线索。

【问题讨论】:

  • 为什么要在测试类中添加运行器?主要跑步者还可以。它将在测试上下文中运行
  • @muasif80 是的,运行器在测试上下文中运行。这正是问题所在。主类中的 Runner 实现需要未在测试上下文中创建的服务(自动配置的测试,如 DataJpaTest 或 WebMvcTest 仅创建原始上下文的一部分,而没有使用 Service 或 Component 注释的类 - 如 spring 文档中所述。在这种情况下,测试应用程序上下文无法由于在主类中缺少 CommandLineRunner impl 中的 BookInputPort 而启动。这会导致上下文失败并导致测试失败。

标签: java spring spring-boot integration-testing


【解决方案1】:

@DataJpaTest 一般从测试类的当前包开始扫描,向上扫描直到找到带有@SpringBootConfiguration 注解的类。

因此,在存储库根包中创建 SpringBootConfiguration 类将只创建在该包中定义的 bean。最重要的是,我们可以在该配置类中添加我们的测试类所需的任何自定义测试 bean。

@SpringBootConfiguration
@EnableAutoConfiguration
public class TestRepositoryConfig {

    @Bean
    public CommandLineRunner commandLineRunner() {
        return args -> {
        };
    }

    @Bean
    public BookInputPort bootInputPort(){
        return new BookInputPort();
    }

}

【讨论】:

    【解决方案2】:

    在 Spring Boot 测试类中你可以这样做

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    public class YourClassTest {
    
        @Autowired
        private YourService yourService;
    

    它确实加载了所有服务,我使用它非常好

    如何在一个环境而不是另一个环境中加载特定实体

    1. 您可以在实体中使用@Profile 注释。
    2. @Profile({"prod"}) 放在您不想在测试运行中加载的实体/服务上,并且
    3. @Profile({"prod", "test"}) 放在要在testprod 环境中加载的实体/服务上
    4. 然后使用test 配置文件运行test。它不会加载不必要的entities
    5. 您也可以在其他服务上添加@Profile 注解。

    【讨论】:

    • 感谢您提供的解决方案。然而,我的目标是运行这个足够用于 JpaRepository 测试的有限上下文。我知道您的解决方案是正确的,并且通常可以解决此问题。我只是想防止加载比需要更多的bean。在这一点上,我不知道我的方法是否可行。
    【解决方案3】:

    注解@DataJpaTest 仅加载Spring Boot 应用程序的JPA 部分,应用程序上下文可能未加载,因为您有@DataJpaTest 注解。尝试将@DataJpaTest 替换为@SpringBootTest

    根据Spring documentation

    默认情况下,带有@DataJpaTest 注释的测试将使用嵌入的 内存数据库(替换任何显式或通常自动配置的 数据源)。 @AutoConfigureTestDatabase 注解可用于 覆盖这些设置。如果您想加载完整的 应用程序配置,但使用嵌入式数据库,您应该 考虑@SpringBootTest 结合@AutoConfigureTestDatabase 而不是这个注解。

    【讨论】:

    • “应用程序上下文可能未加载,因为您有 @DataJpaTest 注释”这正是我想要实现的目标。
    • 我认为这实际上是你的问题。
    • 你可能是对的,它可以由 SpringBootTest 完成。这只是将一些配置外部化(在这种情况下是仅实现 CommandLineRunner 的配置)并在测试类中使用它的问题。我试图理解为什么在嵌套静态类中提供并使用 TestConfiguration 注释的配置有效,而将其外部化到单独的类中却没有。 BR
    • 还值得一提的是,使用@SpringBootTest 加载整个上下文需要更多时间,所以你应该只在必要时这样做,因为最终它会大大减慢你的测试套件
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多