【问题标题】:Spring weirdly depends on class location春天奇怪地取决于上课地点
【发布时间】:2020-04-05 19:20:10
【问题描述】:

我有一个 SpringBootTest 测试,它应该依赖一个单独的类来设置嵌入式 Postgres 和数据源。

所以存储库配置如下所示:

package com.stream.repository.configuration
@Configuration
@ComponentScan(basePackages = arrayOf("com.stream.repository"))
@EntityScan(basePackages = arrayOf("com.stream.repository"))
@EnableJpaRepositories(basePackages = arrayOf("com.stream.repository"))
@EnableAutoConfiguration
class RepositoryConfiguration {

而测试类看起来是这样的:

package com.stream.webapp.rest
@AutoConfigureMockMvc(addFilters = false)
@SpringBootTest(properties =
[
    "spring.jpa.hibernate.ddl-auto=validate",
    "spring.jpa.show-sql=true",
    "spring.liquibase.enabled=true",
    "spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yml",
    "spring.jpa.properties.hibernate.jdbc.time_zone=UTC"
],
        classes = [RepositoryConfiguration::class, AuditController::class],
        webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class AuditControllerTest {

这就是奇怪的地方。如果我使用该配置运行,它会抱怨找不到EntityManagerFactory

AuditService 需要一个找不到的“javax.persistence.EntityManagerFactory”类型的 bean。

经过很多的折腾,我找到了解决这个问题的办法。 如果我将RepositoryConfiguration 移动到包com.stream.webapp.rest 中,即与AuditControllerTest 相同,那么它会神奇地起作用。

我似乎找不到 任何 原因来说明为什么会出现这种情况。那么任何人都可以解释它,有没有办法解决它?因为我不想动它。把它放在它所在的地方很有意义。

作为旁注,它是用 Kotlin 编写的,但我不明白为什么它在这种情况下很重要。 这仅用于测试。在测试范围之外运行应用程序时,它可以工作

我还可以补充一点,AuditControllerTest 在一个模块中,RepositoryConfiguration 在另一个模块中。不确定它是否相关,因为如果它被放置在“正确”的包中(仍然是单独的模块)

TL;DR 的问题:为什么 spring 关心 RepositoryConfigurationAuditControllerTest 在同一个包中?

更新:这是当前配置:(RepositoryConfiguration 不变

@AutoConfigureMockMvc(addFilters = false)
@ComponentScan("com.stream.repository")
@Configuration
@SpringBootTest(properties =
[
    "spring.jpa.hibernate.ddl-auto=validate",
    "spring.jpa.show-sql=true",
    "spring.liquibase.enabled=true",
    "spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yml",
    "spring.jpa.properties.hibernate.jdbc.time_zone=UTC",
    "database.dbname=stream_mapper"
],
        classes = [com.stream.repository.configuration.RepositoryConfiguration::class, ExceptionMapper::class, AuditController::class],
        webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class AuditControllerTest {

【问题讨论】:

  • 在引用 com.stream.repository 包的测试中添加@ComponentScan 注解是否有效?
  • 遗憾的是没有。我尝试将@ComponentScan@ComponentScan("com.stream.repository") 添加到AuditControllerTest 类中,但我仍然收到未找到EntityManagerFactory 的错误
  • 用@Configuration注解怎么样?
  • 看起来也不起作用(同样的错误)。我会用最新的配置更新我的帖子
  • 如果您提供一个最小的、可重现的示例,则更容易识别问题:stackoverflow.com/help/minimal-reproducible-example

标签: java spring-boot testing kotlin spring-boot-test


【解决方案1】:

Spring 对此很奇怪,它会期望测试和主项目的包层次结构相等或大致相等。这样做是最佳做法,以后会省去很多麻烦。

为了配置您的集成测试,请将“BaseIntegrationTest.java”或“AbstractIntegrationTest.java”之类的内容放在与您的主要 Spring Boot 配置相同的包层次结构中,用“test”替换“main”。

例如:

主要配置在这里:

src/main/java/com/stream/repository/configuration/RepositoryConfiguration.java

测试配置会在这里(使用@SpringBootTest)

src/test/java/com/stream/repository/configuration/TestRepositoryConfiguration.java

在上面的结构中,您将大部分(或全部)Spring Boot 测试配置注释放在基础测试配置类 (TestRepositoryConfiguration.java) 上

那么您的实际测试将扩展此类:

例如:

src/test/java/com/stream/webapp/rest/AuditControllerTest.java

将开始:

class AuditControllerTest extends TestRepositoryConfiguration { ... }

此外,您发布的主 RepositoryConfiguration.java 类还有一些奇怪的地方 - 因为它上面有通用的 @EnableAutoConfiguration 注释,在特定于存储库的配置中不需要它 - 它自动包含在 @基础 webapp 中的 SpringBootApplication 注解。

关于您的集成测试,

理想情况下,@SpringBootTest 不应该出现在这个文件中,尽管大多数指南为了简单起见将这个注释放在他们的示例类中。理想情况下,您希望将其放在您的基本集成测试文件(通常是抽象的)中,该文件与定义 @SpringBootApplication 的主应用程序的基本配置文件具有相同的结构(除了测试而不是主文件) - 并保持层次结构测试配置和主配置类的继承类似。

这是一篇关于优化集成测试的精彩文章 - I.E.保持应用程序的一个实例为相同类型的所有测试运行。

https://www.baeldung.com/spring-tests

此外,如果您只需要测试存储库,请考虑使用 @JdbcTest。如果您想从主集成测试中抽象出特定于 repo 的测试,则可以将其保存为不从主应用程序测试扩展的单独的上下文/测试类型。

显然,我无法访问您的整个代码库,因此我提出的任何建议都很难完全解决任何问题,但希望这会让您按照正确的思路思考在结构上设置集成测试以避免许多奇怪的构建错误和解决问题。 (以及以后必须继承您的代码的每个开发人员)...

欢迎评论/提问。

【讨论】:

  • 很遗憾,我在圣诞假期期间无法访问代码,所以我现在无法对此进行测试,但我认为这是一个很好的答案! (还可以补充一点,我在尝试准备一个不会破坏任何 NDA 的“最小”代码示例时遇到了很多问题 :))
  • 感谢您的赏金!如果有帮助,我通常先创建一个 TestApplication 并使用主应用程序的主要配置,然后根据需要为测试定制的内容开始抽象或分离。这里的第一个答案是基本测试类的基本思想:stackoverflow.com/questions/38469706/… 此外,您可以在此测试类或子类中使用 spring autoconfigure 的条件覆盖测试等 bean...我知道,在不输入公司信息的情况下发布需要一个很多时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多