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

news2024/12/30 3:02:24

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生成代码覆盖率报告等,都是提高测试质量和覆盖率的有效手段。通过遵循测试最佳实践,可以进一步提高测试代码的质量和可维护性。

黑马程序员免费预约咨询

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java的诊所医院管理系统,springboot+html,MySQL数据库,用户+医生+管理员三种身份,完美运行,有一万一千字论文

演示视频 基本介绍 基于Java的诊所医院管理系统&#xff0c;springboothtml&#xff0c;MySQL数据库&#xff0c;用户医生管理员三种身份&#xff0c;完美运行&#xff0c;有一万一千字论文。 用户&#xff1a;个人信息管理、预约医生、查看病例、查看公告、充值、支付费用...…

MAVEN-SNAPSHOT和RELEASE

一、快照版本SNAPSHOT和发布版本RELEASE区别 快照版本SNAPSHOT和发布版本RELEASE区别-CSDN博客 在使⽤maven过程中&#xff0c;我们在开发阶段经常性的会有很多公共库处于不稳定状态&#xff0c;随时需要修改并发布&#xff0c;可能⼀天就要发布⼀次&#xff0c;遇到bug时&am…

Scala网络编程:代理设置与Curl库应用实例

在网络编程的世界里&#xff0c;Scala以其强大的并发模型和函数式编程特性&#xff0c;成为了开发者的得力助手。然而&#xff0c;网络请求往往需要通过代理服务器进行&#xff0c;以满足企业安全策略或访问控制的需求。本文将深入探讨如何在Scala中使用Curl库进行网络编程&…

【AI】文心一言的使用分享

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术的飞速发展正在改变我们的生活。文心一言&#xff0c;作为这一浪潮中的佼佼者&#xff0c;以其卓越的自然语言处理能力和广泛的应用场景&#xff0c;给我带来了前所未有的使用体验。在这篇分享中&#xff0c;我…

百万上下文RAG,Agent还能这么玩

❝ 在AI技术飞速发展的今天&#xff0c;我们见证了许多令人惊叹的突破。最近&#xff0c;Qwen2模型的开源引起了广泛的关注&#xff0c;它不仅展示了超越闭源模型的能力&#xff0c;还带来了一个全新的框架——Qwen-Agent。 Qwen-Agent的设计思路虽然与LangChain相似&#xff0…

【光伏科普】走近户用光伏

随着全球对可再生能源和环境保护的日益重视&#xff0c;光伏技术作为一种清洁、可再生的能源形式&#xff0c;受到了广泛的关注和应用。在光伏领域中&#xff0c;户用光伏&#xff08;也称为家庭光伏或屋顶光伏&#xff09;因其灵活性和便捷性&#xff0c;逐渐成为普通家庭实现…

nginx配置https协议(测试环境)

第一步申请证书 首先申请证书这一步&#xff0c;晚上有很多种方式实现&#xff0c;可以自己用算法实现&#xff0c;也可以找在线生成的网站&#xff0c;我这里使用了在线网站 https://www.toolhelper.cn/SSL/SSLGenerate 第二步将证书放到对应的目录下 这里我们主要用cert.pe…

Vue 简单自定义标签

Vue 简单自定义标签 思路&#xff1a; 1、计算每个项离父级左侧宽 left 2、计算当前滑块的宽&#xff0c;绝对定位 3、下一个项的宽/2-滑块的宽/2下一项离父级左侧的宽 left 4、使用定位left&#xff08;性能较差一点&#xff09; 或 translate 移动距离 <template><…

记录pytest中场景执行的token异常处理问题

前言中写了一个conftest钩子函数用于处理重复调用token的方法&#xff0c;http://t.csdnimg.cn/N4rCK&#xff0c;每个用例单独执行都很正常&#xff0c;但是批量执行时一直报错&#xff0c;token缓存处理也不生效。 所有的用例都报获取不到token&#xff0c;方法改了又改&…

一带一路情 相逢《中国缘》-诗琳探访湘西墨戎苗寨交流有感

一带一路情 相逢《中国缘》 诗琳探访湘西墨戎苗寨交流有感 5月21日至25日&#xff0c;《中国缘》栏目组组织的走进湘西苗疆边陲的文化交流活动&#xff0c;在群山环抱、绿树成荫、人文厚重的湘西古丈墨戎苗寨美丽绽放。这场以民间角度推演的中国和中亚人民的文化交流活动&am…

动态规划思想-01背包图解案例

动态规划介绍 动态规划基本思想 ​ 动态规划将一个问题分解为若干个互相重叠的子问题&#xff0c;并通过存储子问题的解来避免重复计算&#xff0c;从而大幅提升时间效率。 ​ 跟分治有些类似&#xff08;“分”与“合”体现在 状态转移方程&#xff09;&#xff0c;但是通常…

区别五大数据可视化工具,有这一篇就够了

进入企业数字化时代&#xff0c;数据可视化工具的重要性被越来越多企业看到。这些企业都希望在短时间内找到适合自己的数据可视化工具。以下是针对帆软BI、奥威BI、思迈特BI&#xff08;Smartbi&#xff09;、永洪BI和亿信华辰BI的详细介绍&#xff0c;希望能帮助用户企业快速筛…

大语言模型 (LLM) 红队测试:提前解决模型漏洞

大型语言模型 (LLM) 的兴起具有变革性&#xff0c;以其在自然语言处理和生成方面具有与人类相似的卓越能力&#xff0c;展现出巨大的潜力。然而&#xff0c;LLM 也被发现存在偏见、提供错误信息或幻觉、生成有害内容&#xff0c;甚至进行欺骗行为的情况。一些备受关注的事件包括…

FL Studio 21.2.2.3914 Win绿色版内置破解补丁和汉化文件,可以完美激活软件

Image-Line FL Studio 21.2.2 x64是一款极受欢迎的音乐制作软件&#xff0c;被广泛用于专业的音乐制作和音频编辑。作为FL Studio系列的最新版本&#xff0c;它在音乐制作界中因其强大的功能、灵活的工作流程和用户友好的界面而备受推崇。 PS.本次为你带来的是fl studio21破解版…

短视频压缩与编码技术在短剧APP小程序开发中的应用:技术选择与工具推荐

在短剧APP小程序开发中&#xff0c;选择合适的短视频压缩与编码技术及工具对于实现高效的视频处理至关重要。本文将探讨如何选择合适的技术和工具&#xff0c;以及推荐一些在实际开发中常用的解决方案。 技术选择的原则 平衡压缩率与视频质量&#xff1a;在选择压缩技术时&…

基于云效 AppStack,5 分钟搞定一个 AI 应用的开发和部署

实验介绍 区别于传统的流水线工具&#xff0c;本实验将带你体验云效应用交付平台 AppStack&#xff0c;从应用视角&#xff0c;完成一个 AI 聊天应用的高效交付。 你将体验到&#xff1a; 基于应用模板快速初始化应用&#xff0c;包含应用的代码库、部署编排架构、变量组、环…

京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设

京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 水库大坝监测系统主要包括渗流监测系统、流量监测系统、雨量监测系统、沉降监测系统组成。每一个监测系统由监测仪器及自动化数据采集装置&#xff08;内置通信装…

virtualbox扩展磁盘

使用virtualbox搭建虚拟机&#xff0c;如果磁盘不够用了&#xff0c;可以通过以下方式扩展。 扩容磁盘 分区扩展 查看磁盘情况 fdisk -l Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sectors Units sectors of 1 * 512 512 bytes Sector size (logical/phys…

HAL库--内存保护(MPU)实验

MPU是内核外设&#xff0c;想获取相关资料只能从内核手册查找 MPU功能仅F7/H7系列具备 内存保护单元(MPU介绍) MPU基本介绍 说白了MPU用来管理内存和外设存储区的访问权限 MPU可配置保护8/16个内存区域(看芯片型号)&#xff0c;每个区域最小要求256字节&#xff0c;且每个区…

【启明智显分享】个位数价格工业HMI芯片:720P@60fps,配备2D加速

我们生活在一个“屏”的时代&#xff0c;工业自动化、智能生活的实现都离不开屏幕的帮助&#xff0c;而对于消费者而言&#xff0c;最大的痛点就是显示屏的画质&#xff0c;一个优质的人机交互界面影响着用户体验&#xff0c;流畅清晰的图像呈现与屏幕的分辨率、刷新率都息息相…