【问题标题】:Spring loads context for each unit test fileSpring为每个单元测试文件加载上下文
【发布时间】:2021-09-24 20:02:41
【问题描述】:

我有 16 个单元测试用例文件。每个都采用这种格式:

@SpringBootTest
class UserServiceTest {

  @Autowired
  UserService userService;

  @MockBean
  SomeDependency someDependency;

  @Test
  ...and so on

}

每当我运行 mvn clean install 时,似乎 spring 正在重新启动/加载上下文 16 次。我看到这些日志 16 次:

[INFO] Running com.base.UserServiceTest
2021-07-16 04:35:39.421 [INFO ] [main] o.s.t.c.s.AbstractTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.base.UserServiceTest], using SpringBootContextLoader
2021-07-16 04:35:39.423 [INFO ] [main] o.s.t.c.s.AbstractContextLoader - Could not detect default resource locations for test class [com.base.UserServiceTest]: no resource found for suffixes {-context.xml, Context.groovy}.
2021-07-16 04:35:39.424 [INFO ] [main] o.s.t.c.s.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.base.UserServiceTest]: UserServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2021-07-16 04:35:39.473 [INFO ] [main] o.s.b.t.c.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.base.Application for test class com.base.UserServiceTest
2021-07-16 04:35:39.475 [INFO ] [main] o.s.t.c.s.AbstractTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
2021-07-16 04:35:39.476 [INFO ] [main] o.s.t.c.s.AbstractTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4dc599a7, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@635f2d9b, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@7e83380f, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@6000fc20, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@1e95926, org.springframework.test.context.support.DirtiesContextTestExecutionListener@1bc08f75, org.springframework.test.context.transaction.TransactionalTestExecutionListener@7ce49f42, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@1b629d02, org.springframework.test.context.event.EventPublishingTestExecutionListener@5089c721, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@4fad5162, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@176bd95a, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@186edc38, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@6c0b0db, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@2343c720, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@6aab6b1b]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.0)

2021-07-16 04:35:39.520 [INFO ] [main] o.s.b.StartupInfoLogger - Starting UserServiceTest using Java 11.0.2 on 
2021-07-16 04:35:39.523 [INFO ] [main] o.s.b.SpringApplication - The following profiles are active: local
2021-07-16 04:35:40.197 [INFO ] [main] o.s.m.w.MockServletContext - Initializing Spring TestDispatcherServlet ''
2021-07-16 04:35:40.197 [INFO ] [main] o.s.w.s.FrameworkServlet - Initializing Servlet ''
2021-07-16 04:35:40.199 [INFO ] [main] o.s.w.s.FrameworkServlet - Completed initialization in 1 ms
2021-07-16 04:35:40.211 [INFO ] [main] o.s.b.StartupInfoLogger - Started UserServiceTest in 0.732 seconds (JVM running for 9.613)
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.918 s - in com.base.UserServiceTest

这会导致两个问题:

  1. 还有很多测试要添加。即使有 16 个测试文件,执行也需要大约 20 秒。所以这个过程很慢。
  2. 我的应用程序使用 JPA 连接 DB,在mvn clean install 期间,虽然应用程序构建成功,但它在日志中随机出现此错误的次数:

2021-07-16 04:35:52.741 [ERROR] [main] c.z.h.p.HikariPool - HikariPool-11 - Exception during pool initialization. org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections

我想知道当我经常执行mvn clean install 时,这种上下文重新加载是否会导致这种情况,并且某处可能存在连接泄漏。

使用 JUnit 5/Jupiter 和 Spring 5。

感谢您在修复以上 2 点方面的任何帮助。谢谢。

【问题讨论】:

  • 你不是单元测试而是集成测试。应该模拟单元测试中的依赖关系,例如数据库和其他东西。 Spring 在其处理集成测试的参考文档中甚至有一个own section。如果这一切都不能让您满意,您始终可以定义自己的 JUnit 套装或跑步者,这些套装或跑步者定义了所需的依赖项,这些依赖项可以由相应的测试使用。
  • @RomanVottner 是的,这就是问题所在。对于单元测试,@ExtendsWith 是可以使用的。
  • 如果你想在 spring 上下文中继续运行而不为每个测试创建一个新的上下文,你可以用 MockInBean 替换你的 MockBean(来自github.com/antoinemeyer/mock-in-bean

标签: java spring spring-boot unit-testing junit


【解决方案1】:

Spring Test caches your TestContext 并在上下文配置适合时将其重新用于另一个测试。

但是,如果您的所有测试都具有不同的设置(例如,第一个测试需要 A 类的模拟版本,而第二个测试需要 B 类的模拟版本),缓存将无济于事,Spring 必须启动一个新的上下文。

如果您简化所有测试的上下文设置,Spring 将重用它,从而加快测试执行速度。

此外,您添加的测试(使用 @SpringBootTest不是单元测试(嗯 - 它始终取决于定义),因为它启动了整个 Spring 上下文。

如果您的计划是为您的业务逻辑编写单元测试,最好只使用 JUnit 5 和 Mockito。这些测试很快,并且没有 Spring 支持,因此没有启动上下文。

有关 Spring Boot 应用程序的单元、集成和端到端测试策略的广泛概述,请查看article

【讨论】:

  • 是的@SpringBootTest 是问题所在。我用@ExtendsWith 更改了它,而不是@MockBean,使用@Mock。那解决了它。谢谢
猜你喜欢
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-07
  • 2020-01-02
  • 2013-01-30
  • 2012-10-28
相关资源
最近更新 更多