Springboot3.x配置类(Configuration)和单元测试

配置类在Spring Boot框架中扮演着关键角色,它使开发者能够利用Java代码定义Bean、设定属性及调整其他Spring相关设置,取代了早期版本中依赖的XML配置文件。

  • 集中化管理 :借助@Configuration注解,Spring Boot让用户能在一个或几个配置类中统一管理所有配置信息。这种方式不仅简化了Bean的定义和配置,还提高了配置管理和维护的效率,避免了分散在多个XML文件中的复杂性。
  • 类型安全配置:由于配置工作是在Java代码内完成,因此可以充分利用Java编译时的类型检查机制,确保配置的准确性。这种做法有效防止了因拼写错误或配置不当引起的运行时错误,增强了应用程序的稳定性和可靠性。
  • 条件化配置能力:该框架还提供了基于条件的配置选项,允许根据特定条件(例如环境变量、系统属性等)动态决定是否创建或配置某个Bean。这项特性增加了应用部署的灵活性,使其更易于适应不同的运行环境和需求变化。
  • 自动装配(Autowiring)支持:Spring Boot内置的自动装配功能可以根据Bean的类型、名称或是限定符自动注入所需的依赖项。这大大减少了手动配置依赖关系的工作量,同时也降低了配置出错的风险。

示例代码

首先定义一个yml的配置文件

spring:
  application:
    name: demo-springboot
  jackson:
    time-zone: Asia/Shanghai
    date-format: yyyy-MM-dd HH:mm:ss
    default-property-inclusion: non_null
server:
  port: 8888
  servlet:
    context-path: /api
logging:
  level:
    root: error
    com.coderlk: debug
app:
  config:
    upload-addr: /opt/upload
    app-key: 12345678

解析配置文件

下面的这个Bean映射到application.yml 中的app:config:xxx的配置

java 复制代码
package com.coderlk.interceptor.demo.config;

import lombok.Data;

@Data
public class AppConfigProperties {
    private String uploadAddr;
    private String appKey;
}

UploadTools.java

java 复制代码
package com.coderlk.interceptor.demo.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class UploadTools {
    private AppConfigProperties appConfigProperties;

    public void upload() {
        log.info("app.config:{}", appConfigProperties);
    }
}

AppConfiguration.java

java 复制代码
package com.coderlk.interceptor.demo.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfiguration {
    
    @Value("${spring.application.name}")
    public String appName;

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    @ConfigurationProperties(prefix = "app.config")
    public AppConfigProperties appConfigProperties() {
        return new AppConfigProperties();
    }

    @Bean
    public AppConfigProperties appConfigPropertiesNon() {
        return new AppConfigProperties();
    }

    @Bean
    public UploadTools uploadTools(@Qualifier("appConfigProperties") AppConfigProperties properties) {
        UploadTools uploadTools = new UploadTools();
        uploadTools.setAppConfigProperties(properties);
        return uploadTools;
    }
}

关键点解析

  • @Configuration :标记该类是一个配置类,其中的方法可以用 @Bean 注解来定义Spring容器管理的bean。
  • **@Value("...")∗∗:用于将配置文件(如'application.yml'或'application.properties')中的值注入到字段、构造函数参数或方法参数中。这里的'...")∗∗:用于将配置文件(如'application .yml '或'application .properties')中的值注入到字段、构造函数参数或方法参数中。这里的'{spring.application.name}`是Spring Boot默认提供的属性之一,代表应用的名字。
  • @Bean :用来声明一个方法,其返回的对象应该被注册为Spring容器中的一个bean。每个@Bean方法实际上就是一种创建bean的方式。
  • @ConfigurationProperties(prefix = "app.config") :允许你将一组特定前缀的配置属性映射到一个Java对象上。这简化了从配置文件读取多个相关属性的过程。在这个例子中,所有以app.config开头的属性都会映射到AppConfigProperties对象中。
  • AppConfigPropertiesUploadTools :是我们自定义的组件类,AppConfigProperties可能用于保存应用程序的配置属性,而UploadTools则可能是负责处理上传逻辑的组件,它依赖于AppConfigProperties来进行某些操作。

测试

请求体

GET http://localhost:8888/api/uploader
Token: 213coiu423m

控制台打印结果

INFO 13428 --- [interceptor-demo] [nio-8888-exec-5] c.c.interceptor.demo.config.UploadTools  : app.config:AppConfigProperties(uploadAddr=/opt/upload, appKey=12345678)

单元测试

Spring Boot 3 与 JUnit 5 的集成使得开发者可以方便地为他们的应用程序编写和运行单元测试及集成测试。以下是有关如何在 Spring Boot 3 中使用 JUnit 5 进行测试的一些关键点:

依赖管理

  1. 确保项目中包含相关依赖
  • 默认情况下,spring-boot-starter-test 依赖项已经包含了 JUnit 5 的支持,因此你通常不需要显式添加 JUnit 5 的依赖。
  • 如果你需要特定版本的 JUnit 5 或者有其他特殊需求,则可以在 pom.xml(对于 Maven 项目)或 build.gradle(对于 Gradle 项目)中添加相应的依赖。
XML 复制代码
<dependencies>
    <!-- Spring Boot Starter Test(包含JUnit 5支持) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

编写测试

  1. 配置JUnit 5
  • Spring Boot 3 默认启用了 JUnit 5,因此只需按照 JUnit 5 的方式编写测试代码即可。你可以直接使用 @SpringBootTest 注解来加载 Spring Boot 应用程序上下文进行集成测试。
  1. 编写测试类
  • 创建一个简单的 JUnit 5 测试类,并使用 @SpringBootTest 加载整个应用上下文。
  • 使用 @Test@BeforeEach@AfterEach 等注解定义测试方法及其生命周期行为。
java 复制代码
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MyApplicationTests {

    @Test
    void test() {
        // 这里写测试逻辑
    }
}

JUnit 5 新特性

  1. JUnit 5 新增特性
  • 注解 :提供了如 @ParameterizedTest@RepeatedTest@DisplayName 等新注解。
  • 断言 :JUnit 5 提供了更加强大的内置断言库,比如 Assertions.assertThat() 和假设条件 Assumptions.assumeThat()
  • 嵌套测试 :通过 @Nested 注解实现更加结构化的测试组织,允许创建内部类来进行分组测试。
  • 扩展模型 :可以通过 @ExtendWith 注解引入自定义扩展,从而增强测试功能。

例如,下面是一个使用了多个新特性的测试例子:

java 复制代码
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;

class StandardTests {

    @BeforeAll
    static void initAll() { /* 初始化 */ }

    @BeforeEach
    void init() { /* 每个测试前执行 */ }

    @DisplayName("成功的测试")
    @Test
    void succeedingTest() { /* 测试逻辑 */ }

    @Test
    void failingTest() {
        fail("一个失败的测试");
    }

    @Test
    @Disabled("为了演示目的禁用")
    void skippedTest() { /* 不会被执行 */ }

    @Test
    void abortedTest() {
        assumeTrue("abc".contains("Z"));
        fail("测试应该被终止");
    }

    @AfterEach
    void tearDown() { /* 每个测试后执行 */ }

    @AfterAll
    static void tearDownAll() { /* 清理工作 */ }

    @Nested
    @DisplayName("当栈是新的时候")
    class WhenNew {

        Stack<Object> stack;

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("是空的")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("抛出异常时弹出")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }
    }
}

综上所述,在 Spring Boot 3 中使用 JUnit 5 可以让你充分利用现代 Java 测试框架的优点,同时保持与 Spring 生态系统的紧密集成。这不仅简化了测试设置过程,还提高了测试代码的质量和可读性。

断言

JUnit 5 提供了一个丰富的断言库,允许开发者编写清晰、表达力强的测试用例。相比之前的版本,JUnit 5 的断言功能更为强大和灵活,支持多种类型的断言以适应不同的测试场景。以下是一些常用的 JUnit 5 断言方法及其使用示例:

基本断言

assertEquals(expected, actual):验证两个值是否相等。

assertTrue(condition) 和 assertFalse(condition):检查条件是否为真或假。

assertNull(object) 和 assertNotNull(object):检查对象是否为 null 或不为 null。

java 复制代码
package com.coderlk.interceptor.demo;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class UnitDemoTest {

    @Test
    void basicAssertions() {
        assertEquals(2 + 2, 4, "简单的加法");
        assertTrue("foo".startsWith("f"), "字符串开始于 'f'");
        assertNull(null, "值应该是null");
    }
}

集合和数组断言

java 复制代码
@Test
void collectionAssertions() {
    String[] expected = {"a", "b", "c"};
    String[] actual = {"a", "b", "c"};
    assertArrayEquals(expected, actual, "数组应该相等");

    List<String> expectedList = Arrays.asList("apple", "banana", "orange");
    List<String> actualList = Arrays.asList("apple", "banana", "orange");
    assertIterableEquals(expectedList, actualList, "列表应该相等");
}

异常处理断言

  • assertThrows(exceptionClass, executable):用于验证代码块是否抛出了预期的异常。
java 复制代码
@Test
void exceptionAssertions() {
    Exception exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("一个非法参数异常");
    });
    assertEquals("一个非法参数异常", exception.getMessage());
}

超时断言

  • assertTimeout(timeout, executable):确保一段代码在指定时间内完成执行。
  • assertTimeoutPreemptively(Duration timeout, Executable executable):强制性地限制一段代码的执行时间,并在超时时立即终止执行。
java 复制代码
@Test
void timeoutAssertions() {
    // 如果这段代码超过1秒,测试将失败
    assertTimeout(Duration.ofSeconds(1), () -> {
        // 执行一些操作...
    });

    // 使用 preemptive 超时策略,如果超时则立即中断执行
    assertTimeoutPreemptively(Duration.ofMillis(100), () -> {
        Thread.sleep(200); // 这个应该会导致超时异常
    });
}

组合断言

  • assertAll(Executable... executables):可以将多个断言组合在一起,在所有断言都通过的情况下才认为整个断言成功。如果任何一个断言失败,则整个测试失败。
java 复制代码
@Test
void multipleAssertions() {
    assertAll(
        () -> assertEquals(2, 2),
        () -> assertTrue("hello".startsWith("he")),
        () -> assertFalse("test".isEmpty())
    );
}

条件断言

  • assumeTrue(boolean condition)assumeFalse(boolean condition):假设条件成立或不成立,如果不满足假设条件,则跳过该测试。
java 复制代码
@Test
void conditionalAssertions() {
    assumeTrue("abc".contains("a"));
    // 如果假设条件不满足,后面的代码不会被执行
    System.out.println("假设条件满足");
}

这些只是 JUnit 5 断言库的一部分功能。JUnit 5 的断言机制非常强大且灵活,能够帮助开发者更有效地进行单元测试和集成测试,确保应用程序的质量和可靠性。如果你需要更加复杂的断言逻辑,还可以考虑使用第三方库如 AssertJ 或 Hamcrest,它们提供了更加丰富的断言功能。

本课程学习代码,同步更新到

https://gitcode.com/fokman/springboot-demo.gitGitCode是面向全球开发者的开源社区,包括原创博客,开源代码托管,代码协作,项目管理等。与开发者社区互动,提升您的研发效率和质量。https://gitcode.com/fokman/springboot-demo.git

相关推荐
十年一梦实验室11 分钟前
【C++】sophus : test_macros.hpp 用于单元测试的宏和辅助函数 (四)
开发语言·c++·单元测试
互联网杂货铺2 天前
单元测试总结
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
梧桐树04292 天前
python:单元测试
开发语言·python·单元测试
溟洵3 天前
【C++第三方库】快速上手---轻量级数据库SQLite和单元测试工具Gtest
数据库·c++·后端·单元测试·sqlite
测试老哥5 天前
如何写出优秀的单元测试?
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
栗子~~6 天前
单元测试-FATAL ERROR in native method: processing of -javaagent failed
单元测试
qq11561487076 天前
单元测试
单元测试
张一西7 天前
ARM学习(35)单元测试框架以及MinGW GCC覆盖率报告
单元测试·mingw·gcc·覆盖率·cppunit·gcov