Java IDEA JUnit 单元测试

news2024/12/23 17:20:01

JUnit是一个开源的 Java 单元测试框架,它使得组织和运行测试代码变得非常简单,利用JUnit可以轻松地编写和执行单元测试,并且可以清楚地看到哪些测试成功,哪些失败

JUnit 还提供了生成测试报告的功能,报告不仅包含测试的成功率,还能统计被测试代码的覆盖率。通过进行单元测试,我们可以确保每个方法按照预期正确运行。

如果我们修改了某个方法的代码,只需要确保相应的单元测试通过,就可以认为修改是正确的。此外,测试代码本身也可以作为示例代码,用于演示如何调用该方法。

几乎所有的IDE工具都集成了JUnit,我们这里使用IDEA

参考 编写JUnit测试 - 廖雪峰的官方网站 (liaoxuefeng.com)

目录

编写JUnit单元测试

使用Fixture自动执行代码

异常测试

条件测试

参数化测试

编写JUnit单元测试

用递推的方法写一个计算n的阶乘的Java方法。

我们可以针对刚刚写的Java编写一个对应的测试代码对其进行测试,在IDEA中可以直接右击点击生成Junit测试。

点击确定生成一个FactorialTest.java文件。

这是JUnit会把带有@Test的方法识别为测试方法,因此需要给测试方法加上@Test注解,测试方法内部用assertEquals(1, Factorial.fact(1))表示期望Factorial.fact(1)返回1。

运行这个测试程序,JUnit就会给出成功的测试和失败的测试,还可以生成测试报告,不仅包含测试的成功率,还可以统计测试的代码覆盖率,即被测试的代码本身有多少经过了测试。

 Factorial.java

public class Factorial {
    public static long fact(long n) {
        long r = 1;
        for (long i = 1; i <= n; i++) {
            r = r * i;
        }
        return r;
    }
}

 FactorialTest.java

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

import org.junit.jupiter.api.Test;

public class FactorialTest {

    @Test
    void testFact() {
        assertEquals(1, Factorial.fact(1));
        assertEquals(2, Factorial.fact(2));
        assertEquals(6, Factorial.fact(3));
        assertEquals(3628800, Factorial.fact(10));
        assertEquals(2432902008176640000L, Factorial.fact(20));
    }

}

使用Fixture自动执行代码

Fixture是JUnit提供的编写测试前准备、测试后清理的固定代码,可以用于测试前和测试后自动执行代码。

先编写一个简单的实现加减法功能的Calculator代码。

但是测试的时候,需要先初始化对象,可以使用@BeforeEach和@AfterEach标记的方法,@BeforeEach标记的方法会在执行每个@Test的方法之前调用,而@AfterEach标记的方法会在执行每个@Test的方法之后调用,这样就可以通过@BeforeEach和@AfterEach标记来自动实现对象的生成和销毁。

然后再编写我们的测试代码。

运行测试代码,可以看到测试结果。

如果需要在所有@Test方法运行前后仅运行一次,那么可以使用@BeforeAll和@AfterAll对方法进行标记。

 Calculator.java

public class Calculator {
    private long n = 0;

    public long add(long x) {
        n = n + x;
        return n;
    }

    public long sub(long x) {
        n = n - x;
        return n;
    }
}

 CalculatorTest.java

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

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;

public class CalculatorTest {

    Calculator calculator;

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

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

    @Test
    void testAdd() {
        assertEquals(100, this.calculator.add(100));
        assertEquals(150, this.calculator.add(50));
        assertEquals(130, this.calculator.add(-20));
    }

    @Test
    void testSub() {
        assertEquals(-100, this.calculator.sub(100));
        assertEquals(-150, this.calculator.sub(50));
        assertEquals(-130, this.calculator.sub(-20));
    }
}

异常测试

对于可能抛出的异常进行测试是测试的重要环节,因此在编写JUnit测试的时候,除了正常的输入输出,还要特别针对可能导致异常的情况进行测试。

在计算阶乘的方法中增加对参数n的检查,如果n为负数,则直接抛出异常IllegalArgumentException。

在测试代码中,我们可以编写一个@Test方法专门测试异常,JUnit提供assertThrows函数来期望捕获一个指定的异常。

运行测试代码,可以看到测试结果。

 Factorial.java

public class Factorial {
    public static long fact(long n) {
        if (n < 0) {
            throw new IllegalArgumentException();
        }
        long r = 1;
        for (long i = 1; i <= n; i++) {
            r = r * i;
        }
        return r;
    }
}

 FactorialTest.java

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

import org.junit.jupiter.api.Test;

public class FactorialTest {

    @Test
    void testFact() {
        assertEquals(1, Factorial.fact(1));
        assertEquals(2, Factorial.fact(2));
        assertEquals(6, Factorial.fact(3));
        assertEquals(3628800, Factorial.fact(10));
        assertEquals(2432902008176640000L, Factorial.fact(20));
    }

    @Test
    void testNegative() {
        assertThrows(IllegalArgumentException.class, () -> {
            Factorial.fact(-1);
        });
    }
}

条件测试

条件测试可以在满足某种条件下执行某些测试方法,不执行某些测试方法。

编写一个程序,该程序中的方法在Windows上跑和在Linux上跑的代码路径不同。

编写测试代码的时候,用@EnableOnOs标记方法,指定只有在特定系统下才执行该测试方法。

用@DisabledOnOs标记方法表示不在某个系统上执行该方法。

用@DisabledOnJre标记方法表示只能在高于特定Java版本的测试。

用@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")标记,表示只能在64位操作系统上执行的测试。

用@EnabledIfEnvironmentVariable标记方法表示需要传入环境变量DEBUG=true才能执行的测试。

运行测试代码,可以看到测试结果。

 Config.java

public class Config {
    public String getConfigFile(String filename) {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            return "C:\\" + filename;
        }
        if (os.contains("mac") || os.contains("linux") || os.contains("unix")) {
            return "/usr/local/" + filename;
        }
        throw new UnsupportedOperationException();
    }
}

 ConfigTest.java

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

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.*;

public class ConfigTest {
    Config config;

    @BeforeEach
    public void setUp() {
        this.config = new Config();
    }

    @AfterEach
    public void tearDown() {
        this.config = null;
    }

    @Test
    @EnabledOnOs(OS.WINDOWS)
    void testWindows() {
        assertEquals("C:\\test.ini", config.getConfigFile("test.ini"));
    }

    @Test
    @EnabledOnOs({OS.LINUX, OS.MAC})
    void testLinuxAndMac() {
        assertEquals("/usr/local/test.cfg", config.getConfigFile("test.cfg"));
    }

    @Test
    @DisabledOnOs(OS.WINDOWS)
    void testOnNonWindowsOs() {
        // TODO: this test is disabled on windows
    }

    @Test
    @DisabledOnJre(JRE.JAVA_8)
    void testOnJava9OrAbove() {
        // TODO: this test is disabled on java 8
    }

    @Test
    @EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
    void testOnlyOn64bitSystem() {
        // TODO: this test is only run on 64 bit system
    }

    @Test
    @EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
    void testOnlyOnDebugMode() {
        // TODO: this test is only run on DEBUG=true
    }
}

参数化测试

JUnit提供了一个@ParameterizedTest注解,用来进行参数化测试。参数化测试和普通测试稍微不同的地方在于,一个测试方法需要接收至少一个参数,然后,传入一组参数反复运行。

编写一个方法,该方法把字符串的第一个字母变为大写,后续字母变为小写。

在编写测试代码的时候,需要给出输入和预期输出,可以通过@MethodSource注解,它允许我们编写一个同名的静态方法来提供测试参数,编写一个静态方法testCapitalize返回了一组测试参数,每个参数都包含两个String,作为测试方法的两个参数传入。

还可以使用@CsvSource标记传入测试参数的方法,它的每一个字符串表示一行,一行包含的若干参数用 , 分隔。

如果测试数据很多,可以把测试数据提到一个独立的CSV文件中,标注上@CsvFileSource表示从CSV文件中读取数据。

由于JUnit只在classpath中查找指定的CSV文件,因此,test-capitalize.csv这个文件要放到src/main/resources目录下,内容格式如下图所示。

运行测试程序,测试结果如下图所示。

 StringUtils.java

public class StringUtils {
    public static String capitalize(String s) {
        if (s.length() == 0) {
            return s;
        }
        return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
    }
}

StringUtilsTest.java

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

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

public class StringUtilsTest {
//    @ParameterizedTest
//    @MethodSource
//    void testCapitalize(String input, String result) {
//        assertEquals(result, StringUtils.capitalize(input));
//    }
//
//    static List<Arguments> testCapitalize() {
//        return List.of( // arguments:
//                Arguments.of("abc", "Abc"), //
//                Arguments.of("APPLE", "Apple"), //
//                Arguments.of("gooD", "Good"));
//    }

//    @ParameterizedTest
//    @CsvSource({"abc, Abc", "APPLE, Apple", "gooD, Good"})
//    void testCapitalize(String input, String result) {
//        assertEquals(result, StringUtils.capitalize(input));
//    }
    @ParameterizedTest
    @CsvFileSource(resources = {"test_capitalize.csv"})
    void testCapitalizeUsingCsvFile(String input, String result) {
        assertEquals(result, StringUtils.capitalize(input));
    }
}

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

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

相关文章

VSCode + vite + vue3断点调试配置

没想到这个配置我搞了一上午&#xff0c;网上很多的配置方案都没有效果。总算搞定了&#xff0c;特此记录一下。 首先需要在.vscode文件夹下面创建launch.json配置文件。然后输入如下配置&#xff1a; {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。//…

Java Swing GUI实现ATM机(涉及网络编程聊天功能)

一、序言 1.首先这是本人大二时期的编程&#xff0c;涉及到网络编程的聊天功能&#xff0c;大佬勿喷。 二、且看展示图片 1.首先启动服务端&#xff08;启动Fuwuduan代码&#xff09;&#xff0c;也就是客服聊天窗口 提供给用户申请银行卡号&#xff0c;客服界面如下&#x…

复试 || 就业day01(2023.12.29)项目一

文章目录 前言正规方程二元一次示例正规方程 : w ( X T X ) − 1 X T y w (X^TX)^{-1}X^Ty w(XTX)−1XTy三元一次方程示例八元一次方程示例sklearn带截距的线性方程总结 前言 &#x1f4ab;你好&#xff0c;我是辰chen&#xff0c;本文旨在准备考研复试或就业 &#x1f4ab;…

unity exe程序置顶和全屏

1.置顶和无边框 设置显示位置和范围 using System; using System.Runtime.InteropServices; using UnityEngine; public class WindowMod : MonoBehaviour {public enum appStyle{FullScreen,WindowedFullScreen,Windowed,WindowedWithoutBorder}public enum zDepth{Normal…

手写Spring与基本原理--简易版

文章目录 手写Spring与基本原理解析简介写一个简单的Bean加载容器定义一个抽象所有类的BeanDefinition定义一个工厂存储所有的类测试 实现Bean的注册定义和获取基于Cglib实现含构造函数的类实例化策略Bean对象注入属性和依赖Bean的功能Spring.xml解析和注册Bean对象实现应用上下…

STM32CubeMX学习(二) USB CDC 双向通信

STM32CubeMX学习&#xff08;二&#xff09; USB CDC 双向通信 简介CubeMX新建工程&#xff08;串口LED&#xff09;测试串口和LED串口接收测试USB CDC通信 简介 利用正点原子F407探索者开发板&#xff0c;测试基于USB CDC的双向数据通信。 CubeMX新建工程&#xff08;串口LE…

ES6+ 面试常问题

一、let const var 的区别 1. var&#xff1a; 没有块级作用域的概念&#xff0c;有函数作用域和全局作用域的概念全局作用域性下创建变量会被挂在到 windows 上存在变量提升同一作用域下&#xff0c;可以重复赋值创建未初始化&#xff0c;值为 undefined 2. let&#xff1a…

2023年末,软件测试面试题总结与分享

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;得准备年后面试了&#xff0c;又不知道从何下手&#xff01;为了帮大家节约时间&#xff0c;特意准备了一份面试相关的资料&#xff0c;内容非常的全面&#xff0c;真的可以好好补一补&#xff0c;希望大家在都能拿到…

天擎终端安全管理系统clientinfobymid存在SQL注入漏洞

产品简介 奇安信天擎终端安全管理系统是面向政企单位推出的一体化终端安全产品解决方案。该产品集防病毒、终端安全管控、终端准入、终端审计、外设管控、EDR等功能于一体&#xff0c;兼容不同操作系统和计算平台&#xff0c;帮助客户实现平台一体化、功能一体化、数据一体化的…

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(16)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识&#xff08;15&#xff09; 1.3 PCI总线的存储器读写总线事务 1.3.5 Delayed传送方式 如前文所述&#xff0c;当处理器使用Non-Posted总线周期对PCI设备进行操作、或者PCI设备使…

Android MVVM 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 ViewModel&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、ViewModel 持有 View 2、ViewModel 持有 Model 3、Model 持有 ViewModel 辅助工具&#xff1a;DataBinding 执行流程&#xff1a;View &g…

linux源码编译升级安装openssl3.0.1导致系统启动失败的问题解决

前两天在安装curl的时候&#xff0c;提示openssl版本太老了&#xff0c;原有的版本是openssl1.0的版本&#xff0c;需要将其升级到openssl3的版本。 直接使用命令行sudo apt install默认安装的还是openssl1.1.1版本&#xff0c;因此决定使用源码自行安装。 具体的安装过程就不赘…

webpack打包批量替换路径(string-replace-webpack-plugin插件)

string-replace-webpack-plugin 是一个用于在 webpack 打包后的文件中替换字符串的插件。它可以用于将特定字符串替换为其他字符串&#xff0c;例如将敏感信息从源代码中移除或对特定文本进行本地化处理。比如文件的html、css、js中的路径地址想批量更改一下 http://localhost:…

海德堡UV灯电源维修eta Plus Elc PE22-400-210

uv灯电源维修故障包括&#xff1a; 1、电压不稳&#xff1a;检查uv打印机的电压&#xff0c;设置一个稳压箱即可。 2、温度过高&#xff1a;uv打印机温度过高也会影响uv灯&#xff0c;可以更换为水冷式循环降温。 3、水箱里的信号线接触不好&#xff1a;将两边的信号线对调&…

leetcode刷题记录07(2023-04-30)【二叉树展开为链表 | 买卖股票的最佳时机 | 二叉树中的最大路径和(递归) | 最长连续序列(并查集)】

114. 二叉树展开为链表 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺…

ArcGIS批量计算shp面积并导出shp数据总面积(建模法)

在处理shp数据时&#xff0c; 又是我们需要知道许多个shp字段的批量计算&#xff0c;例如计算shp的总面积、面积平均值等&#xff0c;但是单个查看shp文件的属性进行汇总过于繁琐&#xff0c;因此可以借助建模批处理来计算。 首先准备数据&#xff1a;一个含有多个shp的文件夹。…

前后端分离nodejs+vue+ElementUi网上订餐系统69b9

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/k…

Lumerical------按键中断程序执行

Lumerical------中断程序执行 引言正文 引言 在 Lumerical 中&#xff0c;很多时候我们需要通过 sweep 的方式来获取我们想要的结果&#xff0c;然而&#xff0c;有时候当我们运行程序后发现书写的脚本有问题时&#xff0c;我们想要强行终止程序的执行&#xff0c;该怎么办呢&…

代码随想录-刷题第四十二天

0-1背包理论基础 0-1背包问题介绍 0-1背包问题&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 0-1背包问题可以使用回溯法进…

Collector收集器的高级用法

Collectors收集器的高级用法 场景1&#xff1a;获取关联的班级名称 原先如果需要通过关联字段拿到其他表的某个字段&#xff0c;只能遍历List匹配获取 for (Student student : studentList) {Long clazzId student.getClazzId();// 遍历班级列表&#xff0c;获取学生对应班级…