Spring Boot / JUnit,为多个配置文件运行所有单元测试

Posted

技术标签:

【中文标题】Spring Boot / JUnit,为多个配置文件运行所有单元测试【英文标题】:Spring Boot / JUnit, run all unit-tests for multiple profiles 【发布时间】:2017-12-25 06:57:30 【问题描述】:

我有一个包含多个测试的 BaseTest 类。每个测试都应针对我列出的每个配置文件执行。

我考虑过使用参数化值,例如:

@RunWith(Parameterized.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// @ActiveProfiles("h2-test") // <-- how to iterate over this?
public abstract class BaseTest 

@Autowired
private TestRepository test;

// to be used with Parameterized/Spring
private TestContextManager testContextManager;

public BaseTest(String profile) 
   System.setProperty("spring.profiles.active", profile);
   // TODO what now?


@Parameterized.Parameters
public static Collection<Object[]> data() 
  Collection<Object[]> params = new ArrayList<>();
  params.add(new Object[] "h2-test" );
  params.add(new Object[] "mysql-test" );
  return params;


@Before 
public void setUp() throws Exception 
  this.testContextManager = new TestContextManager(getClass());
  this.testContextManager.prepareTestInstance(this);
  // maybe I can spinup Spring here with my profile?


@Test
public void testRepository() 
  Assert.assertTrue(test.exists("foo"))

我将如何告诉 Spring 使用这些不同的配置文件运行每个测试?事实上,每个配置文件都会与不同的数据源(内存中的 h2、外部 mysql、外部 oracle 等)通信,因此我的存储库/数据源必须重新初始化。


我知道我可以指定 @ActiveProfiles(...),甚至可以从 BaseTest 扩展并覆盖 ActiveProfile 注释。虽然这会起作用,但我只展示了我的测试套件的一部分。我的很多测试类都是从 BaseTest 扩展而来的,我不想为每个类创建几个不同的配置文件存根。目前工作,但丑陋的解决方案:

BaseTest (@ActiveProfiles("mysql")) FooClassMySQL(来自 BaseTest 的注释) FooClassH2(@ActiveProfiles("h2")) BarClassMySQL(来自 BaseTest 的注释) BarClassH2(@ActiveProfiles("h2"))

谢谢

【问题讨论】:

为什么不使用参数运行所有测试,例如如果你使用 Maven,它可能是mvn test -Dspring.profiles.active=test。我不确定你是否可以通过这个参数化类来实现它,主要是因为 Spring 很可能会在开始执行测试之前启动它的上下文,并且你必须在此之前设置活动配置文件。 谢谢。非常好的解决方案,我没有想过。如果在代码中没有一种优雅的方式来处理它,这肯定会做到!我认为唯一的问题可能是只有少数测试(事实上,我所有的存储库/jpa 测试)需要有不同的配置文件,而其他的不需要/需要访问不同的配置。 酷!如果它适合您,我会将其添加为答案。 这不是我目前正在寻找的答案,但如果没有优雅的代码相关解决方案,我肯定会将其标记为正确;) 近 2 年有代码相关的解决方案吗? 【参考方案1】:

如果您使用 Maven,您实际上可以从命令行(或环境变量,如果需要)指定活动配置文件:

mvn clean test -Dspring.profiles.active=h2-test

参数化测试的方法在这种情况下可能不起作用,因为必须在 Spring 启动其上下文之前指定配置文件。在这种情况下,当您运行参数化集成测试时,上下文将在测试运行程序开始运行您的测试之前启动。此外,JUnit 的参数化测试是出于其他原因(使用不同的数据系列运行单元测试)而发明的。

编辑:还有一件事 - 当您决定使用 @RunWith(Parameterized.class) 时,您将无法使用不同的跑步者。在许多情况下(如果不是全部,如果涉及到集成测试)你想指定不同的运行器,比如 SpringRunner.class - 使用参数化测试你将无法做到。

【讨论】:

我编辑了我的代码,展示了如何解决 SpringRunner.class(主要使用 TestContextManager)【参考方案2】:

弹簧配置文件不适合以这种方式工作。 在您的情况下,每个配置文件都使用特定的数据源。 因此,每一个都需要一个 Spring Boot 负载才能使用预期的数据源运行测试。

实际上,您想要做的就像制作与您想要测试的 Spring 配置文件一样多的 Maven 版本。

此外,在本地环境中构建应该尽可能快。 将自动化测试执行与 DBMS 实现相乘(每个都需要重新加载 Spring Boot)将无济于事。

您不需要指定@ActiveProfiles

它看起来很像持续集成工具的任务,您可以通过指定特定的 Spring Boot 配置文件定义一个执行(顺序或并行)每个 Maven 构建的作业:

mvn clean test -Dspring.profiles.active=h2

mvn clean test -Dspring.profiles.active=mysql

等等……

您也可以尝试通过编写执行 maven 构建的脚本在本地执行它。 但如前所述,它会减慢您的本地构建速度并使其复杂化。

【讨论】:

【参考方案3】:

物有所值:

我的用例是为多个弹簧配置文件运行一个特定的测试类,这就是我实现它的方式:

@SpringBootTest
abstract class BaseTest 
 @Test void doSomeTest() ... ARRANGE-ACT-ASSERT ...


@ActiveProfiles("NextGen")
class NextGenTest extends BaseTest 

@ActiveProfiles("Legacy")
class LegacyTest extends BaseTest 

【讨论】:

以上是关于Spring Boot / JUnit,为多个配置文件运行所有单元测试的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot JUnit 和 @TestPropertySource 使用多个属性文件

Spring Boot 5 多个 JUnit JPA 测试文件接线

JUnit 4 & Spring Boot - 在测试前有选择地重新加载上下文/重新加载 Spring Security 配置

正确配置 Spring Boot 2 和 JUnit 5

Spring Boot 自动装配配置类进入 Junit 测试

未读取junit spring boot配置文件属性