Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java IDEA JUnit 单元测试

Java IDEA JUnit 单元测试

作者头像
叶茂林
发布于 2023-12-30 00:18:09
发布于 2023-12-30 00:18:09
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

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

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

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

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

编写JUnit单元测试

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

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

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

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

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

Factorial.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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));
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Django数据库配置避坑指南:从初始化到生产环境的实战优化
下面命令检查你的项目配置是否符合规范,包括数据库连接。如果数据库配置错误或无法连接,会提示相关错误。
小王子1024
2025/06/30
1070
Django数据库配置避坑指南:从初始化到生产环境的实战优化
一个超级小的 Django 项目.
当用最简单的代码实现 Django 项目为最基本的要素的时候, 项目可以和微框架一样小.
用户1416054
2018/12/12
1.1K0
python测试开发django-1.开始hello world!
当你想走上测试开发之路,用python开发出一个web页面的时候,需要找一个支持python语言的web框架。django框架有丰富的文档和学习资料,也是非常成熟的web开发框架,想学python开发的小伙伴,从django入手是一个不错的选择。本篇写一个简单的“hello world! ”页面,开始django之旅~ 环境准备: Python 3.6.0 django 2.1.2 pycharm
上海-悠悠
2018/11/23
7370
python测试开发django-176.数据库迁移数据(manage.py dumpdata)
manage.py dumpdata 是 django 自带的管理命令,可以用来备份你的模型实例和数据库.
上海-悠悠
2021/12/17
1K0
python测试开发django-176.数据库迁移数据(manage.py dumpdata)
Django 自定义管理命令
Django 提供了一组非常实用的命令, 可以通过django-admin.py和pytohn manage.py脚本调用. 关于这个Management Command的一个优点是你可以创建自定义的command来扩展它.当你需要通过终端命令来对程序进行操作的时候, 通过这个管理命令就非常方便了。 在本篇中, 你将学习到如何编写自己的命令并通过manage.py 来调用.
用户1416054
2018/12/19
1.3K0
Django 自定义管理命令
python manage.py migrate无效的问题
已有的model,修改之后,想重新建模,于是将migrations文件夹中除init.py 之外其他文件都删掉,再次执行以下步骤
用户2936342
2018/08/27
2.9K0
Python 网络服务相关 杂记
[1].Python的原生版Socket [2].python自带的模块:`wsgiref`的简单使用 [3].Python和Idea的爱恨情,pip装了模块但不能用,或飘红了但能用 [4].随手一说 jinja2 [5].django的简单介绍 [6].django中使用MySQL数据库
张风捷特烈
2019/03/20
1.7K0
Python 网络服务相关 杂记
Python:Web框架 Django之manage.py
manage.py是每个Django项目中自动生成的一个用于管理项目的脚本文件,需要通过python命令执行。manage.py接受的是Django提供的内置命令。
Freedom123
2024/03/29
3660
Django来敲门~第一部分【5.2.模型和数据库交互】
通常情况下,如果你只是做测试使用,可以使用Django内置的数据库SQLite就完全可以满足需要了,我们在本次教程中,通过使用MySQL这个数据库来完成后续的功能操作
大牧莫邪
2018/08/27
5170
【Django 2.2 文档系列】django-admin和manage.py
我们在Django开发过程中,命令行执行最多的应该就是python manage.py <command>,今天聊聊manage.py这个命令。
BigYoung小站
2020/05/04
5750
Python笔记:Django框架做web开发(一)
开源!开源!开源大法好! Django除了功能强大之外,最主要的原因是资料丰富,同时Django具有很强的扩展性。 使用Django,你可以在几个小时内将Web应用程序从概念发布到启动。Django负责Web开发的大部分繁琐的事情,所以你可以专注于编写应用程序而无需重新发明轮子。 Django包含许多可用于处理常见Web开发任务的附加功能。Django负责用户身份验证,内容管理,站点地图,RSS源以及更多任务,呼之即来,开箱即用。
Altumn
2019/10/20
1.2K0
【愚公系列】2022年01月 Django商城项目02-Mysql的配置
必须确保Mysql用户表里为空的,如果之前有迁移过数据到Mysql,有可能会出错。注意出错的时候所报的错误信息。如果提示有重复主键,那需要先删掉数据。这些数据是在给MySQL数据库应用迁移文件的时候产生的,一般是content_type相关的表。
愚公搬代码
2022/01/04
3540
【愚公系列】2022年01月 Django商城项目02-Mysql的配置
一日一技:如何让Django 的app migration重新与数据库同步
今天的文章,没有用过Django的同学可能难以理解我在说什么。但是如果你被Django的migration折腾过,那么你一定会感谢这篇文章。
青南
2019/07/23
4.2K1
Python常见web框架汇总
目前,有非常多的Python框架,用来帮助你更轻松的创建web应用。这些框架把相应的模块组织起来,使得构建应用的时候可以更快捷,也不用去关注一些细节(例如socket和协议),所以需要的都在框架里了。接下来我们会介绍不同的选项。
若与
2019/03/01
1.7K0
Python常见web框架汇总
Restful接口开发与测试—Django安装
Django是一个开放源代码的Web应用框架,由Python写成。采用了MVC的框架模式,即模型M,视图V和控制器C。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。
清风穆云
2021/08/09
1.8K0
Django学习-第十一讲(上):ORM迁移命令
将模型生成迁移脚本。模型所在的app,必须放在settings.py中的INSTALLED_APPS中。这个命令有以下几个常用选项:
小海怪的互联网
2019/10/08
1.3K0
1.4 Django基础篇--数据库模型设计
新书小广告 《Python爬虫开发与项目实战》基础篇 试读章节链接: http://pan.baidu.com/s/1hrWEOYg 这本书包括基础篇,中级篇和深入篇三个部分,不仅适合零基础的朋友入门,也适合有一定基础的爬虫爱好者进阶,如果你不会分布式爬虫,不会千万级数据的去重,不会怎么突破反爬虫,不会分析js的加密,这本书会给你惊喜。 前言 从上一节我们知道home应用需要涉及文章,分类和标签三个部分,其实这就是个人博客系统最核心的功能:发表文章。下面我们分析一下数据库
七夜安全博客
2018/06/26
1.3K0
Django学习
Django的基本命令 startproject  创建一个Django项目    django-admin startproject 项目名 startapp      创建一个Django应用    python manage.py startapp 应用名(blog) check         校验项目完整性 runserver     本地建议运行Django项目  python manage.py runserver shell         进入Django项目的python shell环境 test          执行Django用例测试 与数据库相关的命令 makemigrations  创建模型变更的迁移文件   python manage.py makemigrations migrate         执行上一个命令创建的迁移文件  python manage.py migrate dumpdate        把数据库数据导出到文件     loaddate        把文件数据导入到数据库
用户10443079
2023/03/21
7690
Django学习
Django---MTV模型、基本命令、简单配置
MTV模型 Django的MTV分别代表:        Model(模型):负责业务对象与数据库的对象(ORM)        Template(模版):负责如何把页面展示给用户        Vi
用户1214487
2018/01/24
3K0
Django---MTV模型、基本命令、简单配置
【Django】 开发:静态文件,应用和模型层
指定访问静态文件时是需要通过 /static/xxx 或 127.0.0.1:8000/static/xxx
杨丝儿
2022/02/24
2.1K0
【Django】 开发:静态文件,应用和模型层
相关推荐
Django数据库配置避坑指南:从初始化到生产环境的实战优化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验