【Java基础】使⽤Junit5进⾏单元测试
⽂章⽬录
⼀.什么是单元测试呢?
单元测试就是针对最⼩的功能单元编写测试代码。Java程序最⼩的功能单元是⽅法,对Java程序进⾏单元测试就是针对单个Java⽅法的测试。
⼆.测试驱动开发(TDD)
测试驱动开发就是指先编写接⼝,紧接着编写测试。编写完测试后,我们才开始真正编写实现代码。在编写实现代码的过程中,⼀边写,⼀边测,什么时候测试全部通过了,那就表⽰编写的实现完成了.
这就是测试驱动开发TDD(Test-Driven Development)。是敏捷开发中的⼀项核⼼实践和技术。
当然,这是⼀种理想情况。⼤部分情况是我们已经编写了实现代码,需要对已有的代码进⾏测试。
三.JUnit框架
1.为什么需要JUnit框架?
⼀般情况下我们是⽤⼀个main()⽅法在Main⽅法⾥⾯编写测试代码,但使⽤main()⽅法测试有很多缺点:
1. ⼀是⼀个类只能有⼀个main()⽅法,不能把测试代码分离
2. 是没有打印出测试结果和期望结果
3. 是很难编写⼀组通⽤的测试代码。
因此我们可以使⽤JUnit框架进⾏单元测试
2.什么是JUnit框架?
1. JUnit是⼀个开源的Java语⾔的单元测试标准框架,专门针对Java设计,使⽤最⼴泛。
2. 使⽤JUnit编写单元测试的好处在于: 可以⾮常简单地组织测试代码,随时运⾏它们,JUnit就会给出成功的测试和失败的测试,还可
以⽣成测试报告,不仅包含测试的成功率,还可以统计测试的代码覆盖率,即被测试的代码本⾝有多少经过了测试 。对于⾼质量的代码来说,测试覆盖率应该在80%以上。
3. 此外,⼏乎所有的Java开发⼯具都集成了JUnit(如Eclipse,IDEA),这样我们就可以直接在IDE中编写并运⾏JUnit测试。JUnit⽬前最新版
本是JUnit5。
JUnit 5 这个版本,主要特性
提供全新的断⾔和测试注解,⽀持测试类内嵌
更丰富的测试⽅式:⽀持动态测试,重复测试,参数化测试等
实现了模块化,让测试执⾏和测试发现等不同模块解耦,减少依赖
提供对 Java 8 的⽀持,如 Lambda 表达式,Sream API等。
3,单元测试的好处
单元测试可以确保单个⽅法按照正确预期运⾏,如果修改了某个⽅法的代码,只需确保其对应的单元
测试通过,即可认为改动正确。此外,测试代码本⾝就可以作为⽰例代码,⽤来演⽰如何调⽤该⽅法。
使⽤JUnit进⾏单元测试,我们可以使⽤断⾔(Assert)来测试期望结果,可以⽅便地组织和运⾏测试,并⽅便地查看测试结果。
在编写单元测试的时候,我们要遵循⼀定的规范:
1. 单元测试代码本⾝必须⾮常简单,能⼀下看明⽩,决不能再为测试代码编写测试;
2. 每个单元测试应当互相独⽴,不依赖运⾏的顺序;
3. 测试时不但要覆盖常⽤测试⽤例,还要特别注意测试边界条件,例如输⼊为0,null,空字符串""等情况。
四.使⽤Junit5框架
引⼊Junit5框架
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
1.@Test/@DisplayName/@Tag
在⽅法上加上@Test注解,JUnit会把带有@Test的⽅法识别为测试⽅法
@DisplayName: 测试类或⽅法的显⽰名称
@Tag : 为测试类或⽅法添加标签
@Test
@DisplayName("测试⽅法")
@Tag("标签")
public void testJunit(){
System.out.println("HelloWorld");
}
2. 断⾔⽅法
Assert.assertEquals(expected, actual)是最常⽤的测试⽅法,它在Assertions类中定义。Assertions还定义了其他断⾔⽅法,例如:
assertEquals(expected, actual):查看两个对象是否相等。类似于字符串⽐较使⽤的equals()⽅法;
assertNotEquals(first, second):查看两个对象是否不相等。
assertNull(object):查看对象是否为空。
assertNotNull(object):查看对象是否不为空
assertSame(expected, actual):查看两个对象的引⽤是否相等,类似于使⽤“==”⽐较两个对象;
assertNotSame(unexpected, actual):查看两个对象的引⽤是否不相等,类似于使⽤“!=”⽐较两个对象。
assertTrue(String message, boolean condition) 要求condition == true,查看运⾏的结果是否为true;
assertFalse(String message, boolean condition) 要求condition == false,查看运⾏的结果是否为false。
assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual),即查看两个数组是否相等。
assertThat(String reason, T actual, Matcher matcher) :要求matcher.matches(actual) == true,使⽤Matcher做⾃定义的校验。
fail:能使测试⽴即失败,这种断⾔通常⽤于标记某个不应该被到达的分⽀。通常⽤于测试在应该抛出异常的时候确实会抛出异常。
实例代码
public class Factorial {
public static long fact(long n){
long r =1;
for(long i =1; i <= n; i++){
r = r * i;
}
return r;
}
@Test
public void testAssert(){
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));
}
}
测试通过
如果测试结果与预期不符,assertEquals()会抛出异常: 预计返回1111,实际返回1
3. 使⽤Fixture
3.1@BeforeEach/@AfterEach
在⼀个单元测试中,我们经常编写多个@Test⽅法,来分组、分类对⽬标代码进⾏测试。
在测试的时候,我们经常遇到·⼀个对象需要初始化,测试完可能还需要清理的情况。· 如果每个@Test⽅法都写⼀遍这样的重复代码,显然⽐较⿇烦。
JUnit提供处理测试前准备,和测试后清理的公共代码,我们称之为Fixture。
使⽤当前这个类必须先实例化Calculator 对象,才能调⽤相关的⽅法,我们不必在每个测试⽅法中都创建Calculator 对象 通过@BeforeEach来初始化Calculator ,通过@AfterEach来回收Calculator
⽅法描述
@BeforeEach执⾏测试⽅法前调⽤
@AfterEach执⾏测试⽅法后调⽤
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;
}
}
修改后的代码
try catch的使用方法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));
}
}
3.2 @BeforeAll/@AfterAll
⽅法描述
@BeforeAll执⾏所有@Test测试⽅法前调⽤⼀次,只能标注在静态⽅法上⾯
@AfterAll执⾏所有@Test测试⽅法后调⽤ ⼀次,只能标注在静态⽅法上⾯
某些资源初始化和清理会会耗费较长的时间,全局只需要初始化和清理⼀次即可时,例如初始化数据库。JUnit还提供了@BeforeAll和@AfterAll,它们在运⾏所有@Test前后运⾏:
public class DatabaseTest {
static Database db;
//初始化数据库
@BeforeAll
public static void initDatabase(){
db =createDb(...);
}
//关闭数据库
@AfterAll
public static void closeDatabase(){
//...
}
}
3.3.执⾏顺序