JUnit
JUnit介绍
JUnit是Java最基础的测试框架,主要的作用就是断言。 注:Android Studio已经自动依赖了Junit4框架
断言方法:
方法名 | 方法描述 |
---|
assertEquals | 断言传入的预期值与实际值是相等的 | assertNotEquals | 断言传入的预期值与实际值是不相等的 | assertArrayEquals | 断言传入的预期数组与实际数组是相等的 | assertNull | 断言传入的对象是为空 | assertNotNull | 断言传入的对象是不为空 | assertTrue | 断言条件为真 | assertFalse | 断言条件为假 | assertSame | 断言两个对象引用同一个对象,相当于“==” | assertNotSame | 断言两个对象引用不同的对象,相当于“!=” | assertThat | 断言实际值是否满足指定的条件 | fail | 让测试失败 |
注意:上面的每一个方法,都有对应的重载方法,可以在前面加一个String类型的参数,表示如果断言失败时的提示。
assertThat
Unit4.4 结合 Hamcrest 提供了一个新的断言语法——assertThat.使用assertThat的一个断言语句结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想。 也就是说通过assertThat结合Hamcrest 中的匹配符可以完成列表中所有断言方法的作用 assertThat的基本语法assertThat(value, matcher)
Hamcrest
Hamcrest 是一个为了测试为目的,且能组合成灵活表达式的匹配器类库。 Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。 详情请看官方文档
assertThat使用:
注意:需要导入Hamcrest依赖才可以使用 // https://mvnrepository.com/artifact/org.hamcrest/hamcrest-all testImplementation ‘org.hamcrest:hamcrest-all:1.3’ 用法:
数值匹配 assertThat(test1.getShares(), closeTo(1.0,8.8)); //测试变量是否大于指定值 assertThat(test1.getShares(), greaterThan(50)); //测试变量是否小于指定值 assertThat(test1.getShares(), lessThan(100)); //测试变量是否大于等于指定值 assertThat(test1.getShares(), greaterThanOrEqualTo(50)); //测试变量是否小于等于指定值 assertThat(test1.getShares(), lessThanOrEqualTo(100));
//测试所有条件必须成立 assertThat(test1.getShares(), allOf(greaterThan(50),lessThan(100))); //测试只要有一个条件成立 assertThat(test1.getShares(), anyOf(greaterThanOrEqualTo(50), lessThanOrEqualTo(100))); //测试无论什么条件成立(还没明白这个到底是什么意思) assertThat(test1.getShares(), anything()); //测试变量值等于指定值 assertThat(test1.getShares(), is(100)); //测试变量不等于指定值 assertThat(test1.getShares(), not(50));
/字符串匹配/ String url = “http://www.taobao.com”; //测试变量是否包含指定字符 assertThat(url, containsString(“taobao”)); //测试变量是否已指定字符串开头 assertThat(url, startsWith(“http://”)); //测试变量是否以指定字符串结尾 assertThat(url, endsWith(".com")); //测试变量是否等于指定字符串 assertThat(url, equalTo(“http://www.taobao.com”)); //测试变量再忽略大小写的情况下是否等于指定字符串 assertThat(url, equalToIgnoringCase(“http://www.taobao.com”)); //测试变量再忽略头尾任意空格的情况下是否等于指定字符串 assertThat(url, equalToIgnoringWhiteSpace(“http://www.taobao.com”));
集合匹配 List user = new ArrayList(); user.add(test1); user.add(test2); //测试集合中是否还有指定元素 assertThat(user, hasItem(test1)); assertThat(user, hasItem(test2));
Map匹配 Map<String,User> userMap = new HashMap<String,User>(); userMap.put(test1.getUsername(), test1); userMap.put(test2.getUsername(), test2); //测试map中是否还有指定键值对 assertThat(userMap, hasEntry(test1.getUsername(), test1)); //测试map中是否还有指定键 assertThat(userMap, hasKey(test2.getUsername())); //测试map中是否还有指定值 assertThat(userMap, hasValue(test2)); 示例:
class AssertThat { @Test fun test(){
val test1=3.1415926
val test2=200
val test3=90
val test4=60
val test5=100
//测试浮点数是否在1.0~8.8之间
assertThat(test1, closeTo(1.0,8.8));
//测试变量是否大于指定值
assertThat(test2, greaterThan(50));
//测试变量是否小于指定值
assertThat(test3, lessThan(100));
//测试变量是否大于等于指定值
assertThat(test4, greaterThanOrEqualTo(50));
//测试变量是否小于等于指定值
assertThat(test5, lessThanOrEqualTo(100));
}
}
JUnit注解: 首先需要分清几个概念:测试方法、测试类、测试运行器。 测试方法:用@Test注解的一些函数。 测试类:包含一个或多个测试方法的一个Test.java文件。 测试运行器:决定了用什么方式偏好去运行这些测试集/类/方法。 注解名 含义 @Test 指明这是一个测试方法 (@Test注解可以接受2个参数,一个是预期错误 expected,一个是超时时间timeout @Before 在每个测试方法前执行,可做初始化操作 @After 在每个测试方法后执行,可做释放资源操作 @Ignore 忽略的测试方法 @BeforeClass 在该类的所有测试方法和@Before方法之前执行 (修饰的方法必须是静态的) @AfterClass 在该类的所有测试方法和@After 方法之后执行(修饰的方法必须是静态的) @RunWith 指定该测试类使用某个运行器(放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。) @Parameters 指定测试类的测试数据集合 @Rule 重新制定测试类中方法的行为 @FixMethodOrder 指定测试类中方法的执行顺序 执行顺序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
@RunWith
官方描述:When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit. JUnit的用例都是在运行器(Runner)中执行的,通过@RunWith注解就可以指定特定的运行器。JUnti中有默认的运行器,名为BlockJunit4ClassRunner,是在JUnit4.4之后才引入的(在这之前叫做Junit4ClassRunner)。 示例:使用Mockito的运行器
@RunWith(Suite.class) 这个注解的作用是使用JUnit执行一个测试套件。Suite类是JUnit自带的,意为套件,顾名思义,就是一套东西。通过它,可以把多个相关的测试类看做一个测试套件一起测试。 注意:这个套件类本身的用例是不会执行的(类似一个容器)
@RunWith(Suite::class) //该注解用来指明这个一个套件 @Suite.SuiteClasses(TestA::class,TestB::class)//通过该注解指定要执行的测试类(测试类中的所有用例都会执行) class RunWithSuite { //当前这个类中的用例并不会执行
@Test
fun test(){
println("当前这个类中的用例并不会执行")
}
}
@RunWith(Parameterized.class) Parameterized类也是JUnit自带的注解,用于使用多个参数组合多次执行同一个测试用例。 @Parameters 用于JUnit的参数化功能,用来标记准备数据的方法。 注意:该方法需要满足以下要求 该方法必须为public static的 该方法返回值必须为java.util.Collection类型 该方法的名字不做要求 该方法没有参数 解决了什么问题? 当一个测试方法,存在多种可能的情况时,为了避免重复代码, Junit提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。 示例:
@RunWith(Parameterized::class) class RunWithParameterized(var expected: Int, var first: Int, var second: Int) {
companion object {
@JvmStatic
@Parameterized.Parameters
open fun parameters(): List<Array<Int>> {
return listOf(arrayOf(3, 1, 2), arrayOf(5, 2, 3), arrayOf(7, 3, 4), arrayOf(9, 4, 5))
}
}
@Test
fun testAdd(){
println("$expected,$first,$second")
val feature=Feature()
//断言
assertEquals(expected,feature.add(first,second))
}
}
class Feature{ fun add(num1:Int,num2:Int):Int{ return num1+num2 } } 执行结果如下:
可以看到只有一个用例,但是它执行了4遍,这就是Parameterized注解的效果 只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试 @RunWith其他运行器用法参照 @Rule Rule是JUnit4.7加入的新特性,有点类似于拦截器,用于在测试方法执行前后添加额外的处理。实际上是@Before,@After的另一种实现。使用时需要放在实现了TestRule的成员变量上或者返回TestRule的方法上,且修饰符为public。Rule会应用于该类每个测试方法。 内置Rule JUnit提供了很多内置的TestRule的实现满足日常使用,如下: Verifier:所有测试结束后对测试执行结果添加额外的逻辑验证测试最终成功与否。该抽象类为子类提供一个接口方法verify()供扩展 示例:
class Rule { var result=“success”
/*
* 测试用例.
* @Description 测试用例
* @Author 倪海
* @Date 15:30 2021/9/3
* @Param
* @return
**/
@Test
fun verifierTest(){
result="fail"
}
@get:Rule var verifier=object :Verifier(){
override fun verify() {
if (result == "fail"){
throw Exception("failed")
}
}
}
}
运行结果:
ErrorCollector:是Verifier类的一个子类实现,用于在测试执行过程中收集错误信息,不会中断测试,最后调用verify()方法处理。 示例: class RuleErrorCollector {
/*
* ErrorCollector可以收集多个异常,并在方法结束后一起打印出来.
* @Description ErrorCollector可以收集多个异常,并在方法结束后一起打印出来
* @Author 倪海
* @Date 15:45 2021/9/3
* @Param
* @return
**/
@get:Rule var errorCollector=ErrorCollector()
@Test
fun ruleErrorCollector(){
errorCollector.addError(RuntimeException("错误1"))
println("==================表示程序还在运行===================")
errorCollector.addError(RuntimeException("错误1"))
}
}
运行结果:
TemporaryFolder:是抽象类ExternalResource的一个子类实现,用于在JUnit测试执行前后,创建和删除临时目录 示例:
class RuleTemporaryFolder {
/*
* TemporaryFolder:创建临时目录/文件,测试方法执行结束后自动删除.
* @Description TemporaryFolder:创建临时目录/文件,测试方法执行结束后自动删除
* 可以在构造方法中传入使用的父目录,否则默认使用系统临时目录
* @Author 倪海
* @Date 15:50 2021/9/3
* @Param
* @return
**/
@get:Rule val temporaryFolder=TemporaryFolder(File("C:\\Users\\test"))
@Test
fun temporaryFolderTest(){
temporaryFolder.newFolder("test")
temporaryFolder.newFile("test.txt")
Thread.sleep(5000)
}
}
TestName:抽象类TestWatcher的一个子类实现,用于在测试执行过程中获取测试方法名称。在starting()中记录测试方法名,在getMethodName()中返回 示例: class RuleTestName {
//获取用例的方法名
@get:Rule val testName=TestName()
@Test
fun testNameTest(){
println("该示例的方法名为${testName.methodName}")
}
}
运行结果:
TestWatcher:监视测试方法生命周期的各个阶段。该抽象类为子类提供了五个接口方法succeeded(), failed(), skipped(), starting()及finished()供扩展 示例: class RuleTestWatcher {
/*
* TestWatcher:在测试方法开始,结束,成功,失败,跳过这些时间点调用相应方法.
* @Description TestWatcher:在测试方法开始,结束,成功,失败,跳过这些时间点调用相应方法
* @Author 倪海
* @Date 16:00 2021/9/3
* @Param
* @return
**/
@get:Rule val testWatcher=object :TestWatcher(){
override fun apply(base: Statement?, description: Description?): Statement {
return super.apply(base, description)
}
override fun succeeded(description: Description?) {
println("succeeded")
}
override fun failed(e: Throwable?, description: Description?) {
println("failed")
}
override fun skipped(e: AssumptionViolatedException?, description: Description?) {
println("skipped")
}
override fun skipped(
e: org.junit.internal.AssumptionViolatedException?,
description: Description?
) {
println("skipped")
}
override fun starting(description: Description?) {
println("starting")
}
override fun finished(description: Description?) {
println("finished")
}
}
@Test
fun testWatcherTest(){
println("这是一个用例")
}
}
运行结果:
其他内置Rule详见文档
@FixMethodOrder JUnit是通过@FixMethodOrder注解(annotation)来控制测试方法的执行顺序 @FixMethodOrder注解的参数是org.junit.runners.MethodSorters对象,在枚举类org.junit.runners.MethodSorters中定义了如下三种顺序类型: MethodSorters.JVM 按JVM返回的方法名的顺序执行,此种方式下测试方法的执行顺序是不可预测的,即每次运行的顺序可能都不一样。(JDK7里尤其如此),按照JVM返回的方法名执行,并不是按照你方法上下文的顺序执行的,其执行顺序还是不可预期的。 MethodSorters.DEFAULT(默认的顺序) 以确定但不可预期的顺序执行,这是由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定。对于不同操作系统,可能会出现不一样的执行顺序,在某一操作系统上,多次执行的顺序不变 MethodSorters.NAME_ASCENDING 按方法名称的字典顺序执行,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致。不过这种方式需要对测试方法有一定的命名规则,如测试方法均以testNN开头。(NN表示测试方法序列号 01-99) 示例: /*
-
MethodSorters.JVM:按JVM返回的方法名的顺序执行,此种方式下测试方法的执行顺序是不可预测的,即每次运行的顺序可能都不一样 -
MethodSorters.DEFAULT(默认的顺序):以确定但不可预期的顺序执行,这是由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定。 -
MethodSorters.NAME_ASCENDING:按方法名称的字典顺序执行,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致。 **/ @FixMethodOrder(MethodSorters.JVM) class MethodOrder { @Test fun A() { println(“示例A执行了。。。。。。。。。。”) } @Test fun B() { println(“示例B执行了。。。。。。。。。。”) } @Test fun C() { println(“示例C执行了。。。。。。。。。。”) } @Test fun D() { println(“示例D执行了。。。。。。。。。。”) } }
|