首先,免责声明,我对我要问的问题的架构并不完全满意!
我们有一个基于 Spring 的项目,它以 jar 的形式生成一个库。该库包含您可能期望的所有常用控制器/服务/jpa(存储库)层,但没有启动应用程序来启动它。
我们组织内的各个项目可以导入 jar 并立即添加通用 (HTTP) API。
单元测试工作正常。
*IT.java 测试是另一回事。 由 IDE 与 test/java/ 层次结构隔离运行,它们运行正常并通过。
但是,当通过 Maven 作为构建的一部分运行时,它们会失败。 [错误] ControllerIT » IllegalState 无法找到 @SpringBootConfiguration...
我们在测试层次结构中有一个引导配置,IT 在通过 IDE 运行时必须使用该配置,但是在运行 Maven 构建时,似乎无法找到启动应用程序(可以理解,因为它位于测试包中)树)。 我们拥有的启动文件只不过是一个运行库、运行 IT 的测试工具。
我们在 Maven 测试范围中有 h2,但最终库中不需要它(这取决于主机应用程序提供数据源/连接等)。
要求:
运行 mvn install 时的症状 [错误] ControllerIT » IllegalState 无法找到 @SpringBootConfiguration...
大概需要在 pom 中进行一些配置?已经在 Springboot maven 插件配置中使用 package.Boot 。 也许只需要弄清楚神奇的配置,将其指向 src/test/... Boot.class 而不是 src/main/...Boot.class
问题:
spring 是否有一个“正确”的方法来实现我们想要的(我会选择微服务,但由于......原因而不是一个选项)?
当我们不想在产品 jar 中包含可启动类(或 H2 库)时,我们应该如何针对 H2 驱动的 SpringBoot 应用程序实现 IT?
我们是否应该仅为 IT 创建一个单独的 Maven 模块(依赖于库模块)?
缩写的 Maven pom:
<properties>
<version.spring-boot>2.1.13.RELEASE</version.spring-boot>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${version.spring-boot}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.4.0</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- other dependencies excluded for brevity -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${version.spring-boot}</version>
<configuration>
<!-- file lives in test hierarchy -->
<mainClass>org.my.packages.test.Boot</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<skip>true</skip><!-- Skipping unit tests while trying to sort ITs -->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<failIfNoTests>true</failIfNoTests>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</execution>
</plugin>
</plugins>
</build>
Spring Boot 配置:
@SpringBootApplication()
public class Boot extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(Boot.class, args);
}
}
IT配置:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControllerIT
{
@Autowired
private TestRestTemplate restTemplate;
...
}
第二个让我研究@SpringBootTest 的参数。 我最终得到...
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = ITConfig.class)
ITConfig 所在位置:
@SpringBootApplication()
public class ITConfig extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(org.lhasalimited.libraries.ITConfig.class, args);
}
}
这似乎有效。谢谢你的提示。
让我提出一种解决这个问题的方法,可能还有其他方法:
@SpringBootTest
注释旨在模仿成熟的 Spring Boot 应用程序的启动。
它扫描包直到找到 @SpringBootConfiguration
注释(该注释已经放在 @SpringBootApplication
上),这样它就知道应该扫描到找到主类的包(以找到所有的豆子)。
除此之外,它还执行 Spring Boot 应用程序在启动期间执行的许多其他操作:加载遵循 Spring Boot 应用程序约定的配置(application.properties/yaml)、加载自动配置(
spring.factories
中的内容)等等。
最重要的是,您将在测试中加载一个成熟的 Spring Boot 应用程序(通常是微服务)。
到目前为止一切顺利,但你说你并没有真正拥有 Spring Boot 应用程序。所以我看到三种方法:
在
@SpringBootConfiguration
文件夹中引入一个带有src/test/java/whatever-package-close-to-root
的人工类(注意,它在'test'中,而不是在src/main
中)
将
@SpringBootTest
与“配置”参数一起使用。这意味着 SpringBootTest
不会使用所有这些上下扫描过程,而是指示它使用具体的上下文配置。
根本不要使用
@SpringBootTest
注释,而更喜欢“通常”@ContextConfiguration
- 是的,您不会拥有 Spring Boot 的功能,但正如我上面所说,您可能不需要它。此外,这些测试会更快,特别是如果您提供许多“@Configuration”类并且在测试期间仅加载库的“相关”部分。
例如,如果您测试 DAO,则加载 Web 相关内容是没有意义的。