侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Junit | 不会写单元测试,就如同不穿秋裤的熊孩子在冬天瞎跑

2022-07-30 星期六 / 0 评论 / 0 点赞 / 245 阅读 / 19103 字

在我以往的 Android 开发生涯中,几乎没有使用过单元测试,也没有见过有人去介绍过,好像这个东西在国内开发者眼里并不是很重要,或者说大多数开发同学没有专门的时间去使用单元测试框架,也许更重要的原因

.

在我以往的 Android 开发生涯中,几乎没有使用过单元测试,也没有见过有人去介绍过,好像这个东西在国内开发者眼里并不是很重要,或者说大多数开发同学没有专门的时间去使用单元测试框架,也许更重要的原因应该是我个人的 孤陋寡闻

.

背景

什么是单元测试?

单元测试是针对最小的单元编写测试代码。在 Java 中,最小的功能单位是方法,因此,对Java 程序进行单元测试就是针对单个 Java 方法的测试。

为什么要做单元测试

在国外,实际开发流程往往是,先编写测试,测试写完后,再开始真正编写实现代码。在具体实现过程中,一边写一边测,什么时候测试全部通过,就代表开发任务完成。这也就是我们常说的 TDD(测试驱动开发)。


简介

Junit 是一个开源的Java语言的单元测试框架,专门为 Java 设计,使用也最为广泛。(当然 Kotlin 使用也没问题,注意一些小细节即可)

依赖方式

Maven

<dependencies> 	<dependency>    	<groupId>junit</groupId>    	<artifactId>junit</artifactId>    	<version>4.12</version>    	<scope>test</scope>	</dependency></dependencies>

Gradle

dependencies {	testImplementation 'junit:junit:4.12'}

主要方法

Assert类中主要方法如下:

方法名方法描述
assertEquals断言传入的预期值与实际值是相等的
assertNotEquals断言传入的预期值与实际值是不相等的
assertArrayEquals断言传入的预期数组与实际数组是相等的
assertNull断言传入的对象是为空
assertNotNull断言传入的对象是不为空
assertTrue断言条件为真
assertFalse断言条件为假
assertSame断言两个对象引用同一个对象,相当于“==”
assertNotSame断言两个对象引用不同的对象,相当于“!=”
assertThat断言实际值是否满足指定的条件

注意 上面的所有方法,都有对应的重载方法,可以在前面加一个 String 类型的参数,表示断言失败时的提示。


常用注解

执行顺序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

注解名含义
@Test表示此方法为测试方法
@Before在每个测试方法前执行,可做初始化操作
@After在每个测试方法后执行,可做释放资源操作
@Ignore忽略的测试方法
@BeforeClass在类中所有方法前运行。此注解修饰的方法必须是static void
@AfterClass在类中最后运行。此注解修饰的方法必须是static void
@RunWith指定该测试类使用某个运行器
@Parameters指定测试类的测试数据集合
@Rule重新制定测试类中方法的行为
@FixMethodOrder指定测试类中方法的执行顺序

使用方式

基础使用

比如我们有一个等效括号的这样一个算法。

StackExample.kt

/** 等效括号 *  如题:给定一个字符串所表示的括号序列,包含以下字符: '(', ')', '{', '}', '[' and ']', 判定是否是有效的括号序列。 *  括号必须依照 "()" 顺序表示, "()[]{}" 是有效的括号,但 "([)]" 则是无效的括号。 * *  解法思路: *  使用栈存储,将字符串切割为char遍历,先存储指定方向的符号,如'(','{','['。 *  如果属于右方向的,如'}'等,进入判断,如果栈顶符号与当前char相等并且栈不会null,即为正确,否则直接return false * */fun isBrackets(str: String): Boolean {    if (str.length < 2) return false    val stack = Stack<Char>()    str.forEach {        if ("{([".contains(it)) {            stack.push(it)        } else {            if (stack.isNotEmpty() && isBracketChart(stack.peek(), it)) {                stack.pop()            } else return false        }    }    return stack.isEmpty()}private fun isBracketChart(str1: Char, str2: Char): Boolean =    (str1 == '{' && str2 == '}') ||            (str1 == '[' && str2 == ']') || (str1 == '(' && str2 == ')')

代码测试(未使用Junit)

如果是没有使用 Junit,我们可能会写出下面这样的测试代码:

fun main() {    println(isBrackets("{}"))  	xxxx...}

相比来说我们如果我们增加别的方法,就需要频繁修改main()方法,而且对于测试的正确性也不能做到直观。

使用Junit

我们在相应的test包下,新建 StackExampleKtTest 这样的类,或者直接使用如下快捷方式,在相应的方法前使用mac(option+回车),windows(ctrl+回车),如图所示

示例如下:

StackExampleKtTest

class StackExampleKtTest {    @Test    fun testIsBrackets() {        assertArrayEquals(            booleanArrayOf(                isBrackets(""),                isBrackets("{"),                isBrackets("{{}}"),                isBrackets("{({})}"),                isBrackets("{({}"),            ), booleanArrayOf(                false, false, true, true,false            )        )    }}

参数化测试

上述使用方法,如果我们每次测试一个方法都要去设置对应的值,相对比较繁琐,那如何用连续不同的值去测试同一个方法呢,这样就可以避免我们不去多次修改,节省部分时间。这时就要使用 @RunWith@Parameters.

首先需要在测试类上添加 RunWith(Paramterized.class) 注解,在创建一个由 @Paramters 注解的 static 方法,让返回一个对应的测试数据合集,最后创建构造方法,方法的参数顺序和测试数据集合一一对应。

示例如下:

@RunWith(Parameterized::class)class StackExampleKtTest(private val str: Pair<String, Boolean>) {    // TODO: 2020/11/15 相应的,这种处理方式也容易造成对错误的难以寻找    companion object {        @Parameterized.Parameters        @JvmStatic        fun primeStrings(): Collection<Pair<String, Boolean>> =            listOf(                "" to false,                "{" to false,                "{}" to false,                "{[]}" to true,                "asd{()}" to false            )    }    @Test    fun testIsBrackets() {      	//注意这里的错误提示        assertEquals("出错了-当前 /"${str.first}/" to ${str.second}",            str.second, isBrackets(str.first))    }}

注意:相应的 @Parameterized.Parameters 方法在Kotlin中使用需要增加 @JvmStatic 。使用过程中,这种参数化测试如果我们没有加错误提示,寻找问题时可能不容易找到那个测试用例出了问题,所以这点也需要注意。

assertThat用法

用于为断言失败后的输出信息提高可读性。默认情况下,断言失败只会抛出 AssertionError ,我们无法知道到底是哪里出错,而 assertThat 的作用就是解决这个问题。

常用的匹配器整理:

匹配器说明例子
is断言参数等于后面给出的匹配表达式assertThat(5, is (5));
not断言参数不等于后面给出的匹配表达式assertThat(5, not(6));
equalTo断言参数相等assertThat(30, equalTo(30));
equalToIgnoringCase断言字符串相等忽略大小写assertThat(“Ab”, equalToIgnoringCase(“ab”));
containsString断言字符串包含某字符串assertThat(“abc”, containsString(“bc”));
startsWith断言字符串以某字符串开始assertThat(“abc”, startsWith(“a”));
endsWith断言字符串以某字符串结束assertThat(“abc”, endsWith(“c”));
nullValue断言参数的值为nullassertThat(null, nullValue());
notNullValue断言参数的值不为nullassertThat(“abc”, notNullValue());
greaterThan断言参数大于assertThat(4, greaterThan(3));
lessThan断言参数小于assertThat(4, lessThan(6));
greaterThanOrEqualTo断言参数大于等于assertThat(4, greaterThanOrEqualTo(3));
lessThanOrEqualTo断言参数小于等于assertThat(4, lessThanOrEqualTo(6));
closeTo断言浮点型数在某一范围内assertThat(4.0, closeTo(2.6, 4.3));
allOf断言符合所有条件,相当于&&assertThat(4,allOf(greaterThan(3), lessThan(6)));
anyOf断言符合某一条件,相当于或assertThat(4,anyOf(greaterThan(9), lessThan(6)));
hasKey断言Map集合含有此键assertThat(map, hasKey(“key”));
hasValue断言Map集合含有此值assertThat(map, hasValue(value));
hasItem断言迭代对象含有此元素assertThat(list, hasItem(element));

@Rule

在测试过程中,我们也可以通过增加 @Before 或者 @After 从而做到测试前后的一个提示效果,但是每次都这样写也许有点麻烦。所以这个时候可以使用 @Rule.

示例如下:

TestPrompt

 class TestPormpt : TestRule {    override fun apply(statement: Statement, description: Description): Statement {        // 获取测试方法的名字        val methodName: String = description.methodName        //相当于 @Before        println(methodName + "测试开始前!")        // 运行的测试方法        statement.evaluate()        //运行结束,相当于@After        println(methodName + "测试结束!")        return statement    }}
class StackExampleKtTest {    // TODO: 2020/11/15    //  注意:在 Kotlin 中使用时,需要将@Rule 更改为 @get:Rule    //  或者 使用 @Rule @JvmField    //@get:Rule    @Rule @JvmField    public val prompt = TestPormpt()  //    @Before//    fun testStart(){//        println("测试开")//    }////    @After//    fun testStop(){//        println("测试关")//    }    @Test    fun testThat() {        assertThat("123", equalTo("123"))    }}

参考

.
.

广告 广告

评论区