SpringBoot:以最少的配置运行 Junit 单元测试

Posted

技术标签:

【中文标题】SpringBoot:以最少的配置运行 Junit 单元测试【英文标题】:SpringBoot: Running a Junit unit test with minimal configuration 【发布时间】:2020-05-23 13:36:54 【问题描述】:

我有一个Springboot 项目,在那里我找到了一种方法来创建和运行简单的Junit 测试用例,它查看存储库并获取给定实体的一些数据属性。 Junit 运行的结果是通过,所以这没有问题。

但事情就在这里,我已经看到很多例子,其中教程显示Springboot 项目,他们可以简单地运行Junit 测试,只有@Runwith@SpringBootTest 为他们的特定测试课程。

在我的情况下,我必须添加 3 个注释,@SpringBootTest@RunWith 以及 @ContextConfiguation(with parameters),直到我能够运行测试用例。

所以我的问题是我如何才能尽可能简约地运行它,(我见过的一些练习对于他们的 springboot 测试类只有一个注释)

我的Springboot 测试类如下所示:

Screenshot of my Junit class

我的目录结构如下所示:

Screenshot of my Project directory structure

我的application.properties 看起来像这样:

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/erfan
spring.datasource.username=erfan
spring.datasource.password=

#Some additional properties is trying to be set by Spring framework so this must be set
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true


#spring.datasource.initialization-mode=always
#spring.datasource.initialize=true
#spring.datasource.schema=classpath:/schema.sql
#spring.datasource.continue-on-error=true 

#HikariCP is a ConnectionPool manager, related to DB stuff



#Below is the property key you need to set to * as value to expose all kind of monitoring related information
#about your web application 

#management.endpoints.web.exposure.include=*

还有我的pom.xml 文件:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
    </parent>
    <groupId>com.sample</groupId>
    <artifactId>postgres</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>postgres</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</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-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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-actuator</artifactId>
        </dependency>



    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>



        </plugins>






    </build>
</project>

那么我的 application.properties 文件中是否缺少某些内容?为了能够在我的测试类中删除“样板”注释,我应该包含哪些内容?

【问题讨论】:

准确地说:如果您的测试涉及查看实际存储库,那么它肯定不是单元测试。 @Amadán 我同意。我只是对我的应用程序无法按我预期的方式运行的原因感到好奇 【参考方案1】:

取决于你想要做什么。基本上,spring 具有自定义注释,可将 spring 上下文配置为仅包含相关的 bean。这就是所谓的test slices

但我总是尝试遵循一些“规则”:

避免使用@SpringBootTest,除非您正在进行集成测试,或者手动设置要使用的类@SpringBootTest(classes = MyService1.class, MyService2.class 如果你在测试spring jpa,你可以使用@DataJpaTest注解,例如here 如果您正在测试控制器,您可以使用@WebMvcTest,例如here 如果您正在测试其他服务,您可以随时使用@ContextConfiguration 来相应地配置 Spring 上下文。

例如,对于您的测试,我会以两种方式之一编写它:

@RunWith(SpringRunner.class)
@DataJpaTest
@Import(AnotherConfig.class)
class MyTest 
   // test stuff here

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AnotherConfig.class)
// NOTE, if you have a JpaConfig class where you @EnableJpaRepositories
// you can instead add this config class to the @ContextConfiguration classes
@EnableJpaRepositories
class MyTest 
   // test stuff here

基本上,不必担心在测试之上有多少注释,而要担心哪些 bean/服务正在被自动装配。例如,@SpringBootTest 是一个注解,但会自动装配 spring 上下文中的所有 bean。

【讨论】:

非常感谢。我现在也明白了,你有多少注释并不重要。它只会让你的代码看起来很糟糕并且没有完整的功能。我发现 Springboot 需要按特定顺序声明组件和 bean 的包,以使应用程序按照您期望的方式运行。否则会失败。【参考方案2】:

我强烈建议不要在单元测试中使用一堆 spring 注释。单元测试应该只测试一段代码,与外部或其他层无关,所以Mockito 应该足够了。

例子:

@RunWith(MockitoJUnitRunner.class)
public class FooTest 
   @InjectMocks
   private FooService service;

   @Mock
   private FooRepository repository;

   @Test
   public void whenHappyPath_shouldReturnTrue()
      doReturn(Optional.empty()).when(repository).findById(anyLong());
      assertTrue(service.isFoo(1L));
   

您正在阻止您的单元测试到达存储库层,因此您不需要使用嵌入式数据库或任何其他东西创建上下文。

如果您用于集成测试,那么情况就不同了,您将需要不同的策略。为此,我建议在测试中使用嵌入式数据库(如果您有 h2 依赖项,则默认情况下会这样做):

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

还可以使用integrationtest 弹簧轮廓:

@ActiveProfile("test") // or integration, you choose
public class FooIntegrationTest
   ...

或强制其他配置文件指向另一个配置

@TestPropertySource(properties =  "spring.config.location=classpath:application-test.yml" )

application-test.properties

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop

【讨论】:

非常感谢。由于我是 Springboot 的新手,我仍然不知何故处于在哪里可以找到东西以及如何配置东西的困惑状态,所以我的问题可能听起来很愚蠢(对此感到抱歉),但我正处于探索阶段。无论如何,我发现的东西指向了你的一些观点,即 Springboot 在你创建包的位置等方面很敏感,所以你不能期望以与某些应用程序显示的方式相同的方式运行它,因为它们有不同的布局结构。【参考方案3】:

Erfan,这完全取决于你的测试场景。

第一个场景:完整测试(集成测试)

如果你想测试整个应用,比如测试Service层、Repository层和Controller层,你需要一个真正的spring-context,所以你必须使用所有@SpringBootTest@RunWith和。 .. 初始化 spring 上下文以测试整个层。 (这称为集成测试)

Unit Test vs Integration Test: What's the Difference

how-to-use-java-integration-testing

第二个场景:单元测试

如果您只想测试一段代码,就像您只想测试服务层和其他层(如存储库)在您的场景中并不重要,在这种情况下,您必须使用一些新框架,如 @987654327 @,模拟你不想测试的部分,在这些场景中你不需要**spring-context初始化**所以你不需要使用@SpringBootTest或其他注释。

Mockito Sample

因此,根据您的情况,您可以使用这些注释。


我强烈建议您阅读以下链接,以获取有关 Java 测试最佳实践的更多信息。

Modern Best Practices for Testing in Java

【讨论】:

非常感谢。我想我已经想通了。 Springboot 似乎对你放置东西的位置很敏感(创建包并声明组件和 bean),所以如果你没有按照 Springboot 的构建方式得到它,那么你将无法按预期运行它。

以上是关于SpringBoot:以最少的配置运行 Junit 单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot 和 JUnit 测试中定义 spring.config.location?

Spring认证指南:了解如何以最少的配置构建应用程序

如何在单个 JUnit 测试中运行两个 Spring Boot 微服务?

springBoot测试类报Failed to resolve org.junit...错误

为啥不能在 junit 配置中添加程序参数?

springBoot是啥?