当前位置: 首页 > article >正文

java中的单元测试的使用以及原理

目前在写单元测试的时候,在测试类中依赖到了需要spring管理的bean,但是没有使用spring的测试支持,导致无法自动注入这些依赖,下面通过这个机会了解一下@SpringBootTest这个注解的原理。

规范的测试程序:

@SpringBootTest
public class ApiTest {
    private final XXXMapper mapper;

    @Autowired
    public ApiTest(XXXMapper mapper) {
        this.mappermapper = mapper;
    }

    @Test
    public void deleteDupliteTranslationsTest( ) {
       mapper.xxx();
    }
}


仅使用junit,运行测试程序的时候会报错:

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter 

需要引入的依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>testtest</scope>
</dependency>
<dependency>
   <groupId>org.junit.jupiter</groupId>
   <artifactId>junit-jupiter-api</artifactId>
   <version>5.8.2</version>
   <scope>test</scope>
</dependency>

一. 单元测试

以下是关于Java中单元测试的详细介绍:

单元测试的概念

单元测试是软件开发中的一种测试方法,旨在对软件中的**最小可测试单元(如方法、类等)进行验证。在Java中,通常是针对一个类的方法进行测试**,检查方法的输入与输出是否符合预期,以确保代码的正确性和可靠性。

单元测试的重要性

  • 保证代码质量:通过对每个单元进行独立测试,能在开发早期发现代码中的缺陷和错误,避免问题在后续集成和系统测试阶段扩大,降低修复成本。
  • 提高代码可维护性:编写良好的单元测试可以使代码结构更加清晰,模块之间的耦合度更低。当需要对代码进行修改或扩展时,单元测试可以帮助开发者快速验证修改是否对其他部分产生影响。
  • 支持重构:有了完善的单元测试,开发者在对代码进行重构时可以更加放心,因为可以通过运行单元测试来确保重构后的代码功能仍然正确。
  • 促进代码设计:在编写单元测试的过程中,开发者需要考虑代码的可测试性,这会促使他们设计出更加易于测试、解耦和模块化的代码。

常用的单元测试框架

  • JUnit:是Java中最流行的单元测试框架之一。它提供了一组注解和断言方法,用于编写和运行单元测试。例如,@Test注解用于标记测试方法,assertEquals方法用于验证方法的返回值是否与预期值相等。以下是一个简单的JUnit测试示例:
import org.junit.jupiter.api.Test;

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

public class CalculatorTest {

    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        int result = calculator.add(5, 3);
        assertEquals(8, result);
    }
}

class Calculator {
    public int add(int num1, int num2) {
        return num1 + num2;
    }
}
  • TestNG:与JUnit类似,但功能更加强大,支持更多的测试功能,如参数化测试、数据驱动测试、分组测试等。它使用注解来定义测试方法和测试套件。以下是一个TestNG的参数化测试示例:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class CalculatorTest {

    @Test(dataProvider = "addData")
    public void testAdd(int num1, int num2, int expected) {
        Calculator calculator = new Calculator();
        int result = calculator.add(num1, num2);
        assertEquals(result, expected);
    }

    @DataProvider(name = "addData")
    public Object[][] addData() {
        return new Object[][]{
                {5, 3, 8},
                {10, 20, 30},
                {-5, 5, 0}
        };
    }
}

class Calculator {
    public int add(int num1, int num2) {
        return num1 + num2;
    }
}

单元测试的最佳实践

  • 保持测试的独立性:每个单元测试应该独立于其他测试,不依赖于其他测试的执行顺序或状态。
  • 编写清晰的测试方法名:测试方法名应该能够清晰地表达测试的目的,例如testAddPositiveNumberstestDivideByZero等。
  • 全面覆盖各种情况:尽可能覆盖各种边界情况、异常情况和正常情况,确保代码在各种输入下都能正确运行。
  • 及时更新测试:当代码发生变化时,及时更新相应的单元测试,确保测试的有效性。
  • 避免测试中的副作用:测试方法不应该对系统的其他部分产生持久的副作用,例如修改数据库记录、文件等。

二.@SpringBootTest 注解的原理

1. 启动 Spring 上下文

  • 触发机制
    • 当你在测试类上添加 @SpringBootTest 注解时,JUnit 5 的 <font style="color:#DF2A3F;">SpringExtension</font> 会拦截测试类的执行过程。SpringExtension 是 JUnit 5 提供的一个扩展,专门用于集成 Spring 框架。
    • 在测试开始前,SpringExtension 会**启动一个 Spring 应用程序上下文**。这个上下文类似于你在运行 Spring Boot 应用程序时启动的上下文,会根据你的配置(例如 application.propertiesapplication.yml)、配置类(使用 @Configuration 注解的类)以及 Spring Boot 的自动配置机制来创建和管理 Bean。

2. 依赖注入

  • 自动扫描和 Bean 管理
    • Spring 会扫描你的项目,根据 @Component@Service@Repository@Controller 等**注解创建相应的 Bean,并将它们存储在应用程序上下文中**。这些 Bean 是 Spring 管理的对象实例,它们的生命周期由 Spring 控制,包括依赖注入、初始化、销毁等。
    • 对于测试类中使用 @Autowired 注解的属性或构造函数参数,Spring 会自动从上下文中查找匹配的 Bean,并将其注入到相应的位置。
    • 在你的测试类中,使用 @Autowired 注入 和 xxxMapper 时,Spring 会在启动的上下文中查找相应的 Bean 实例,并将它们注入到测试类的构造函数中。这是因为这些 Mapper 通常会在你的 Spring 应用中被标记为 @Mapper@Repository 等注解,从而被 Spring 识别和管理。

3. 测试执行

  • 上下文共享和重用
    • Spring 会根据测试类的需求,决定是否创建新的上下文或重用现有的上下文。在某些情况下,多个测试类可能共享同一个上下文,以提高性能。例如,如果多个测试类使用相同的配置和依赖,Spring 会尝试复用已经创建的上下文,避免重复创建相同的 Bean,从而加快测试执行速度。
    • 当执行测试方法时,由于所需的依赖已经通过 @Autowired 注入到测试类中,测试方法可以使用这些依赖进行测试。

4. 与 Spring Boot 的集成

  • 配置和自动配置
    • Spring Boot 的自动配置机制会根据项目的依赖和配置自动配置一些 Bean。例如,如果你添加了 spring-boot-starter-data-jpa 依赖,Spring Boot 会自动配置 JPA 相关的 Bean,如 EntityManagerFactoryJpaTransactionManager 等。
    • @SpringBootTest 注解会利用 Spring Boot 的这些自动配置功能,确保测试环境与实际应用环境尽可能相似,包括加载配置文件、启用配置类等。这样可以保证测试的准确性和可靠性,因为测试是在与实际运行环境相似的条件下进行的。

总结

  • @SpringBootTest 注解的核心原理是利用** Spring 框架和 Spring Boot 的自动配置机制,在测试类执行前启动一个 Spring 应用程序上下文,然后根据 **<font style="color:#DF2A3F;">@Autowired</font>** 等注解将上下文中的 Bean 注入到测试类中。**
  • 这样做可以让你的测试代码方便地使用 Spring 管理的资源和服务,确保测试环境与实际应用环境的一致性,同时充分利用 Spring 的依赖注入功能,使测试更加方便和可靠。
  • 此外,你可以通过该注解的各种属性来进一步定制测试环境,以满足不同的测试需求,如测试不同的配置场景、不同的 Web 环境等。

三.Spring 应用程序上下文

以下是关于 Spring 应用程序上下文的详细解释:

1. 容器概念

  • 定义
    • Spring 应用程序上下文(ApplicationContext)是 Spring 框架的核心容器,它负责管理对象(Bean)的创建、配置和生命周期。可以将其看作是一个高级容器,比 BeanFactory 更强大和灵活,它不仅包含了 BeanFactory 的功能,还提供了更多的企业级特性,如**事件发布、国际化支持、资源加载、消息解析等。**

2. Bean 的存储和管理

  • 存储
    • 应用程序上下文存储了 Spring 管理的 Bean 实例。当你使用 Spring 开发应用程序时,会在类上添加诸如 <font style="color:#DF2A3F;">@Component</font><font style="color:#DF2A3F;">@Service</font><font style="color:#DF2A3F;">@Repository</font><font style="color:#DF2A3F;">@Controller</font> 等注解,这些类会被 Spring 识别为 Bean 候选者。Spring 应用程序上下文会将这些类实例化为 Bean 并存储起来。
    • 例如,你有一个 UserService 类被标注为 @Service
import org.springframework.stereotype.Service;


@Service
public class UserService {
    // 服务的方法
}
- 当 Spring 应用程序上下文启动时,它会创建一个 `UserService` 的实例,并将其存储在容器中,这个实例就是一个 Bean。

3. Bean 的创建和配置

  • 创建
    • 应用程序上下文会根据配置信息创建 Bean。这些配置信息可以来自多个方面,包括:
      • 组件扫描:使用 @ComponentScan 注解,Spring 会扫描指定包下的类,对于标注了 @Component 及其衍生注解(如 @Service@Repository@Controller)的类,会创建相应的 Bean。
      • Java 配置类:使用 @Configuration 注解的类,其中通过 @Bean 注解的方法可以创建 Bean。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }
}
    - **XML 配置**:在 XML 文件中配置 Bean,虽然这种方式在现代 Spring 开发中使用较少,但在一些遗留系统中可能存在:
<beans>
    <bean id="userService" class="com.example.UserService"/>
</beans>

- **配置元数据的合并**:应用程序上下文会综合考虑上述各种配置信息,最终决定哪些类需要创建为 Bean 以及如何创建它们。

4. Bean 的生命周期管理

  • 初始化
    • 当 Bean 被创建后,Spring 可以调用其初始化方法,这可以通过以下几种方式实现:
      • 在 Bean 的类中实现 <font style="color:#DF2A3F;">InitializingBean</font> 接口并实现 **<font style="color:#DF2A3F;">afterPropertiesSet</font>**<font style="color:#DF2A3F;">()</font> 方法;
      • 使用 @PostConstruct 注解标注一个方法;
      • 在 XML 配置中指定 init-method
  • 依赖注入
    • 应用程序上下文会自动将一个 Bean 所依赖的其他 Bean 注入到该 Bean 中,这可以通过构造函数注入、属性注入或方法注入实现。例如,假设 UserService 依赖 UserRepository
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
- Spring 会在创建 `UserService` 时,找到 `UserRepository` 的 Bean 并将其注入到 `UserService` 的构造函数中。

‼️ 这里的注入指的是将某个bean所依赖的bean注入到这个bean的构造函数中,这样在这个bean中就可以使用所依赖的bean,这也是为什么会出现循环依赖的问题。

  • 销毁
    • 当应用程序关闭或上下文被销毁时,Spring 可以调用 Bean 的销毁方法,这可以通过实现 DisposableBean 接口的 destroy() 方法,或者使用 @PreDestroy 注解,或者在 XML 中指定 destroy-method 来实现。

5. 作用域

  • 不同作用域
    • Spring 应用程序上下文可以管理 Bean 的作用域,常见的作用域有:
      • Singleton:默认作用域,在整个应用程序上下文中,一个 Bean 只有一个实例。
      • Prototype:每次请求 Bean 时都会创建一个新的实例。
      • Request:在一个 HTTP 请求中,一个 Bean 只有一个实例(仅适用于 Web 应用)。
      • Session:在一个 HTTP 会话中,一个 Bean 只有一个实例(仅适用于 Web 应用)。

6. 高级特性

  • 事件发布
    • 应用程序上下文可以作为事件发布者,你可以定义事件监听器来监听和处理事件。例如:
import org.springframework.context.ApplicationEvent;


public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }
}


import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;


@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        // 处理事件
    }
}


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;


@Service
public class EventPublisherService {

    @Autowired
    private ApplicationEventPublisher publisher;

    public void publishEvent() {
        publisher.publishEvent(new MyEvent(this));
    }
}
- 这里 `EventPublisherService` 可以发布 `MyEvent`,而 `MyEventListener` 会监听并处理 `MyEvent`。
  • 资源加载:
    • 可以使用应用程序上下文加载各种资源,如文件、URL 等。例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;


@Service
public class ResourceService {

    @Autowired
    private ApplicationContext context;

    public void loadResource() throws Exception {
        Resource resource = context.getResource("classpath:myfile.txt");
        // 处理资源
    }
}

总结

  • Spring 应用程序上下文是一个强大的容器,它存储和管理 Spring 中的 Bean,包括创建、配置和管理它们的生命周期。
  • 它综合了多种配置信息,如组件扫描、Java 配置和 XML 配置,以创建所需的 Bean。
  • 它还提供了依赖注入、事件发布、资源加载等高级特性,使得开发人员可以更方便地开发复杂的企业级应用程序,同时也方便了应用程序的测试和维护。

应用程序上下文在测试中的使用

  • 在测试中使用 @SpringBootTest 时,应用程序上下文会根据你的测试需求启动,为测试类中的 @Autowired 字段注入所需的 Bean。这样可以确保测试代码可以使用 Spring 管理的服务和资源,就像在实际应用中一样,提高测试的可靠性和有效性。同时,也方便你测试 Spring 组件之间的交互和依赖,因为这些组件可以从上下文获取所需的 Bean 实例。

总之,Spring 应用程序上下文是 Spring 框架的核心,它为应用程序的开发、运行和测试提供了强大的基础架构,使开发人员能够更专注于业务逻辑而不是对象的创建和管理等细节。


http://www.kler.cn/a/514642.html

相关文章:

  • MyBatis最佳实践:提升数据库交互效率的秘密武器
  • 即现软著工具 - 让软著申请更高效
  • MYSQL数据库基础-01.数据库的基本操作
  • mysql之表的外键约束
  • pthread_exit函数
  • 脚本工具:PYTHON
  • 机器学习-线性回归(简单回归、多元回归)
  • Java爬虫还有其他用途吗?
  • 头歌实训作业 算法设计与分析-贪心算法(第3关:活动安排问题)
  • cling: c++交互式执行
  • 数据分析 基础定义
  • 深入探讨Web应用开发:从前端到后端的全栈实践
  • 无人机反制设备:察打诱一体设备技术详解
  • Linux:修改用户名
  • 5.9 洞察 OpenAI - Translator:日志(Logger)模块的 “时光记录仪”
  • 「全网最细 + 实战源码案例」设计模式——单例设计模式
  • 深度学习 Pytorch 动态计算图与梯度下降入门
  • HTTPS协议简述
  • Flask基础和URL映射
  • 【spring专题】编译spring5.3源码
  • 如何给自己的域名配置免费的HTTPS How to configure free HTTPS for your domain name
  • ERP系统的财务会计基础知识:财务管理
  • Kmeans与KMedoids聚类对比以及python实现
  • C语言中危险函数
  • JMeter 测试Dubbo 接口
  • Win10系统部署RabbitMQ Server