快速掌握JUnit等测试框架的使用,进行Java单元测试

1. 单元测试简介

单元测试(Unit Testing)是一种软件测试方法,通过对软件中的最小可测试单元进行验证,确保它们按预期工作。单元测试通常用于测试一个类的单个方法,以确保其逻辑正确、边界情况处理妥当、异常处理合适。单元测试的主要目标是提高代码质量,减少错误,并提高代码的可维护性和可测试性。

2. JUnit简介

JUnit是Java平台上最流行的单元测试框架之一。JUnit提供了一套丰富的注解和断言方法,方便开发者编写和执行单元测试。JUnit的核心概念包括测试类、测试方法、断言和注解。

3. 安装和设置JUnit

3.1 Maven项目

如果你使用Maven构建项目,可以在pom.xml文件中添加JUnit依赖:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>
3.2 Gradle项目

如果你使用Gradle构建项目,可以在build.gradle文件中添加JUnit依赖:

testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'

4. JUnit 5基础用法

4.1 基本注解
  • @Test:标识一个测试方法。
  • @BeforeEach:在每个测试方法执行前执行。
  • @AfterEach:在每个测试方法执行后执行。
  • @BeforeAll:在所有测试方法执行前执行,仅运行一次。
  • @AfterAll:在所有测试方法执行后执行,仅运行一次。
4.2 编写简单测试

下面是一个简单的测试示例,展示了如何使用JUnit 5进行单元测试。

import org.junit.jupiter.api.*;

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

public class CalculatorTest {

    private Calculator calculator;

    @BeforeEach
    public void setUp() {
        calculator = new Calculator();
    }

    @Test
    public void testAdd() {
        int result = calculator.add(2, 3);
        assertEquals(5, result, "2 + 3 should equal 5");
    }

    @Test
    public void testSubtract() {
        int result = calculator.subtract(5, 3);
        assertEquals(2, result, "5 - 3 should equal 2");
    }

    @AfterEach
    public void tearDown() {
        calculator = null;
    }
}

在这个示例中,CalculatorTest类包含两个测试方法testAddtestSubtract,分别测试Calculator类的addsubtract方法。@BeforeEach注解的方法在每个测试方法执行前运行,以初始化测试环境。

5. JUnit断言

JUnit提供了一组丰富的断言方法,用于验证测试结果。常用的断言方法包括:

  • assertEquals(expected, actual):验证两个值是否相等。
  • assertNotEquals(unexpected, actual):验证两个值是否不等。
  • assertTrue(condition):验证条件是否为真。
  • assertFalse(condition):验证条件是否为假。
  • assertNull(object):验证对象是否为null。
  • assertNotNull(object):验证对象是否不为null。
  • assertThrows(expectedType, executable):验证执行的代码是否抛出指定的异常。
@Test
public void testAssertions() {
    // 断言两个值相等
    assertEquals(4, 2 + 2);

    // 断言条件为真
    assertTrue(5 > 3);

    // 断言对象不为空
    assertNotNull(new Object());

    // 断言抛出指定异常
    assertThrows(ArithmeticException.class, () -> {
        int result = 1 / 0;
    });
}

6. 参数化测试

参数化测试允许使用不同的参数多次运行同一个测试方法。JUnit 5提供了@ParameterizedTest注解和一些参数源注解,如@ValueSource@CsvSource等,用于实现参数化测试。

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

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

public class ParameterizedTestExample {

    @ParameterizedTest
    @ValueSource(ints = {1, 2, 3, 4, 5})
    public void testIsPositive(int number) {
        assertTrue(number > 0, "Number should be positive");
    }
}

在这个示例中,testIsPositive方法使用不同的参数(1到5)运行多次,以验证每个参数都大于0。

7. 测试生命周期

测试生命周期注解用于在测试方法执行前后进行一些准备和清理工作。

  • @BeforeEach:在每个测试方法执行前运行。
  • @AfterEach:在每个测试方法执行后运行。
  • @BeforeAll:在所有测试方法执行前运行,仅运行一次。
  • @AfterAll:在所有测试方法执行后运行,仅运行一次。
import org.junit.jupiter.api.*;

public class LifecycleTest {

    @BeforeAll
    public static void initAll() {
        System.out.println("Before all tests");
    }

    @BeforeEach
    public void init() {
        System.out.println("Before each test");
    }

    @Test
    public void testOne() {
        System.out.println("Test one");
    }

    @Test
    public void testTwo() {
        System.out.println("Test two");
    }

    @AfterEach
    public void tearDown() {
        System.out.println("After each test");
    }

    @AfterAll
    public static void tearDownAll() {
        System.out.println("After all tests");
    }
}

运行上述代码时,输出将显示测试生命周期的执行顺序。

8. 测试异常和超时

在测试中,验证方法是否正确处理异常和超时情况非常重要。

8.1 测试异常

可以使用assertThrows方法验证方法是否抛出指定的异常。

@Test
public void testException() {
    Exception exception = assertThrows(ArithmeticException.class, () -> {
        int result = 1 / 0;
    });
    assertEquals("/ by zero", exception.getMessage());
}
8.2 测试超时

可以使用@Timeout注解设置测试方法的执行时间限制。

import org.junit.jupiter.api.Timeout;

import java.time.Duration;

@Test
@Timeout(1)  // 单位为秒
public void testTimeout() throws InterruptedException {
    Thread.sleep(500);  // 模拟一些耗时操作
}

如果测试方法在指定时间内没有完成,将会失败。

9. Mocking

在单元测试中,有时需要模拟(mock)对象的行为,以便在不依赖真实对象的情况下进行测试。Mockito是一个流行的Java mocking框架。

9.1 引入Mockito依赖

在Maven项目中添加Mockito依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.7.7</version>
    <scope>test</scope>
</dependency>

在Gradle项目中添加Mockito依赖:

testImplementation 'org.mockito:mockito-core:3.7.7'
9.2 使用Mockito进行Mocking

下面是一个使用Mockito的示例:

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

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class MockitoExampleTest {

    @Mock
    private Calculator calculator;

    @InjectMocks
    private CalculatorService calculatorService;

    public MockitoExampleTest() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testAdd() {
        when(calculator.add(2, 3)).thenReturn(5);
        
        int result = calculatorService.add(2, 3);
        assertEquals(5, result);

        verify(calculator).add(2, 3);
    }
}

在这个示例中,我们使用@Mock注解创建一个Calculator的mock对象,并使用@InjectMocks注解将其注入到CalculatorService中。when方法用于定义mock对象的行为,verify方法用于验证mock对象的交互。

10. 集成测试

虽然单元测试主要用于验证单个类或方法的功能,但集成测试则用于验证多个组件之间的交互。JUnit也可以用于编写集成测试。

10.1 使用Spring进行集成测试

Spring框架提供了强大的测试支持,使得编写和执行集成测试变得更加简单。通过@SpringBootTest注解,我们可以启动Spring应用上下文并进行测试。

添加Spring测试依赖(如果使用Maven):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

使用Spring进行集成测试的示例:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

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

@SpringBootTest
public class CalculatorServiceIntegrationTest {

    @Autowired
    private CalculatorService calculatorService;

    @MockBean
    private Calculator calculator;

    @Test
    public void testAdd() {
        when(calculator.add(2, 3)).thenReturn(5);

        int result = calculatorService.add(2, 3);
        assertEquals(5, result);
    }
}

在这个示例中,我们使用@SpringBootTest注解来启动Spring应用上下文,并使用@MockBean注解创建一个mock对象。在测试方法中,我们定义了mock对象的行为并验证了服务层的逻辑。

11. 代码覆盖率

代码覆盖率是衡量测试完整性的重要指标。它显示了测试覆盖了多少代码,可以帮助我们找出未被测试的代码部分。

11.1 使用JaCoCo

JaCoCo是一个流行的Java代码覆盖率工具。它可以与Maven和Gradle集成,用于生成代码覆盖率报告。

在Maven项目中添加JaCoCo插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>report</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

运行Maven命令生成代码覆盖率报告:

mvn clean test
mvn jacoco:report

在Gradle项目中应用JaCoCo插件:

plugins {
    id 'jacoco'
}

jacoco {
    toolVersion = "0.8.6"
}

test {
    useJUnitPlatform()
    finalizedBy jacocoTestReport
}

jacocoTestReport {
    reports {
        xml.required = true
        html.required = true
    }
}

运行Gradle命令生成代码覆盖率报告:

./gradlew test jacocoTestReport

生成的报告将显示哪些代码被测试覆盖,哪些代码没有覆盖。

12. 测试最佳实践

12.1 保持测试独立

每个测试方法应该是独立的,不应该依赖其他测试方法的执行结果。这样可以确保每个测试都能单独运行,并且容易调试和维护。

12.2 使用有意义的测试名称

测试方法的名称应该清晰地描述测试的目的和预期行为。这样可以使测试代码更加可读,并且在测试失败时可以更容易地理解问题所在。

12.3 测试边界情况

在编写单元测试时,不仅要测试正常的输入,还要测试边界情况和异常情况。这可以确保代码在各种情况下都能正常工作。

12.4 避免使用静态变量

在单元测试中使用静态变量可能会导致测试之间的相互影响,从而引入难以调试的问题。尽量避免在测试代码中使用静态变量。

12.5 定期运行测试

定期运行测试可以帮助及时发现代码中的问题,特别是在进行代码重构或添加新功能时。持续集成(CI)系统可以自动化运行测试,并生成测试报告。

单元测试是软件开发过程中至关重要的一部分。它通过验证最小的可测试单元,确保代码的正确性和稳定性。JUnit作为Java平台上最流行的单元测试框架,提供了丰富的注解和断言方法,方便开发者编写和执行单元测试。

此外,使用Mockito进行mocking、使用Spring进行集成测试、使用JaCoCo生成代码覆盖率报告等,都是提高测试质量和覆盖率的有效手段。通过遵循测试最佳实践,可以进一步提高测试代码的质量和可维护性。

黑马程序员免费预约咨询

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/707886.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计Python+Django农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop Hive

课题研究的意义&#xff0c;国内外研究现状、水平和发展趋势 研究意义21世纪是一个信息爆炸的时代&#xff0c;人们在日常生活中可接触到的信息量非常之巨大。推荐系统逐步发展&#xff0c;其中又以个性化推荐系统最为瞩目。个性化推荐系统的核心在于个性化推荐算法&#xff0c…

李永乐线代笔记

线性方程组 解方程组的变换就是矩阵初等行变换 三秩相等 方程组系数矩阵的行秩列秩&#xff0c;线性相关的问题应求列秩&#xff0c;但求行秩方便 齐次线性方程组 对应向量组的线性相关&#xff0c;所以回顾下线性相关的知识&#xff1a; 其中k是x&#xff0c;所以用向…

组态软件之万维组态介绍(web组态、html组态、vue2/vue3组态、组态软件、组态编辑器)

一、什么是组态软件 组态软件是一种用于创建、配置和管理监控和控制系统的软件工具。组态是指不需要编写计算机程序、通过配置的方式完成工业应用开发的系统。它们通常用于工业自动化领域&#xff0c;用于实时监视和控制工业过程。组态软件提供了丰富的功能和工具&#xff0c;使…

【漏洞复现】飞企互联-FE企业运营管理平台 treeXml.jsp SQL注入漏洞

0x01 产品简介 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据、物联网、移动互联网等技术支撑的云工作台。这个平台可以连接人、链接端、联通内外&#xff0c;支持企业B2B、C2B与020等核心需求&#xff0c;为不同行业客户的互联网转型提供支持。其特色在于提供…

原型模式(大话设计模式)C/C++版本

原型模式 C 参考&#xff1a;https://www.cnblogs.com/Galesaur-wcy/p/15924300.html #include <iostream> #include <string> using namespace std;class WorkExprerience { private:string workDate;string company;public:WorkExprerience() {}~WorkExprerie…

收银系统源码-分销商城功能!

在当今的数字化时代&#xff0c;分销已经成为了一种非常流行的商业模式&#xff0c;它与我们的生活息息相关。 分销&#xff0c;简单来说&#xff0c;就是通过一定的渠道和方式&#xff0c;将商品或服务推广给更多的人&#xff0c;从而实现销售增长的过程。在生活中&#xff0…

告别照片丢失!掌握4个实用技巧,找回相册不再困扰

手机里的相册记录了我们的瞬间&#xff0c;捕捉了我们的笑容&#xff0c;让我们的回忆变得生动而具体。然而&#xff0c;随着科技的飞速发展&#xff0c;照片丢失的问题也随之而来。每当我们不小心删除了心爱的照片&#xff0c;那种失落感可能会让我们感到无所适从。那么&#…

Elasticsearch:智能 RAG,获取周围分块

作者&#xff1a;来自 Elastic Sunile Manjee 在检索增强生成 (RAG) 领域&#xff0c;一个持续存在的挑战是找到输入大型语言模型 (LLM) 的最佳数据量。数据太少会导致响应不足或不准确&#xff0c;而数据太多会导致答案模糊。这种微妙的平衡启发我开发了一个专注于智能分块和利…

免费听歌,电脑或手机免费听歌,落雪音乐安装详细步骤

近年来&#xff0c;由于资本的力量导致各种收费&#xff0c;看个电视想听歌都必须要付费了&#xff0c;否则你听不完整&#xff0c;吃相非常难看&#xff0c;特别是电视&#xff0c;吸血鬼式吸收各种会员费&#xff0c;各种APP也是铺天盖地的广告&#xff0c;渐渐迷失了自我&am…

用C语言实现扫雷

本篇适用于C语言初学者&#xff0c;主要涉及对于函数&#xff0c;数组&#xff0c;分支循环的运用。 目录 设计思想&#xff1a; 总代码&#xff08;改进后&#xff09;&#xff1a; 运行结果展示&#xff1a; 分布介绍&#xff1a; 声明&#xff1a; 代码主体部分&#…

三个pdf工具和浏览软件(pdftk,muppdf,epdfview)

安装pdftk pdftk是一款功能强大的PDF处理工具&#xff0c;主要用于对PDF文件进行各种操作。它提供了丰富的功能&#xff0c;包括但不限于合并、拆分、旋转、加密、解密、添加水印、从PDF文档中解出附件等。pdftk分为图形界面版本和命令行版本&#xff0c;适用于不同的用户需求…

计算机网络(6) TCP协议

TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是互联网协议套件中一种核心协议。它提供面向连接的、可靠的字节流传输服务&#xff0c;确保数据从一端正确无误地传输到另一端。TCP的主要特点包括&#xff1a; 可靠性&#xff1a;TCP使用…

AIGC绘画设计:Midjourney V6 来袭,该版本有哪些新功能?

Midjourney V6 支持更自然的语言输入&#xff0c;可以处理更自然地对话式&#xff08;以前的版本是以关键字为中心的&#xff09;提示&#xff0c;对复杂提示有了更好的解释能力。大幅增加了每个 /image 的内存&#xff0c;可以处理更长、更详细的提示&#xff08;从40 直接提升…

一文理清sshc包的使用场景和掌握两种连接方式及异常场景

一文理清sshc、ssh包的使用场景和两种连接方式 SSH协议SSH&#xff08;Secure Shell&#xff09;协议支持通过多种编程语言实现客户端和服务端的功能&#xff0c;包括Go、Python、Java、C#等。 GO语言 sshc包的使用建立连接1.DialWithKey2.DialWithPasswd 运行命令异常场景思维…

北斗车载终端TD30助力户外出行现代化、信息化——保障无信号区域通信、实时精准定位

据官方统计&#xff0c;截至2023年9月底&#xff0c;全国机动车保有量达4.3亿辆&#xff0c;其中汽车3.3亿辆&#xff0c;新能源汽车1821万辆&#xff1b;机动车驾驶人5.2亿人&#xff0c;其中汽车驾驶人4.8亿人。车辆持有率的上升也带来车辆安全的考量&#xff0c;再者交通运输…

【linux】给net/socket.c部分接口添加pr_info后运行情况

net/socket.c 合入文件及代码&#xff1a; https://gitee.com/r77683962/linux-6.9.0/commit/d9aca07352311a9c185cbc2d3c39894e02f10df3 开机后dmesg命令运行效果&#xff1a; 这也是一部分&#xff0c;不过从这里看出来&#xff0c;添加打印日志的地方不太好&#xff0c;另…

CTFshow-web sql注入

Web171 1 在题目中可以看到查询语句为 "select username,password from user where username !flag and id ".$_GET[id]." limit 1;"; 直接使用万能密码 查到了所有用户 获得flag Web172 0 可以看到返回逻辑显示 如果返回的查询数据中username不等于fl…

随着Midjourney越来越成熟,它将给现实世界带来哪些影响?

Midjourney使用的过程中&#xff0c;你是不是会遇到这样的情况&#xff1f; 生成出来的图片压根不是自己想要的&#xff1f; 想要的风格根本不知道怎么写提示词&#xff1f; 不用担心&#xff0c;今天白白给大家带来了280种酷炫动作咒语&#xff0c;拿去&#xff0c;都能生成…

诊所管理系统哪家会好一点

随着医疗行业的快速发展和信息化进程的加速&#xff0c;诊所作为医疗服务的重要基层单位&#xff0c;其运营管理效率与服务质量的提升愈发依赖于现代化的管理工具。诊所管理系统应运而生&#xff0c;旨在通过集成化、智能化的技术手段&#xff0c;帮助诊所实现诊疗流程优化、资…