单元测试概述

概念

单元测试是为了验证代码中某个类或者方法能否按照正常输入得到预期的输出。一个情况下我们会测试共有方法即public方法,如果需要测试私有方法,需要通过mokc的方式,才能测试,在实际开发中我们已经会编写一些工具类,如果没有单元测试的助力,我们在实际测试过程中,可能对边界值的测试不足,导致线上出现问题。

通过单元测试,可以准确的保证应用的稳定性,和功能的正确性。随着应用模块的不断跟新迭代,测试单元也要跟着不断变化,保证应用模块的单元测试覆盖率以及迭代的稳定性。单元测试测试的主要范围是类的共有方法,保障公有方法有预期的行为。

优点:

  1. 快速验证方法的正确性,主要是边界值的测试,能否根据输入值,获取正确的输出。
  2. 推动代码架构的更新迭代。在单元测试过程中,如果类的职责不够单一,不利于单元测试,这样可以反推代码架构的更新迭代。
  3. 单元测试,保障了应用的稳定和更新迭代的效率。在Android开发过程中,虽然Gradle做了大量的编译优化工作,但是随着代码量的增加,每次编译程序的时间都会增加。但是基于JVM本地的单元测试可以省略编辑环节,直接测试修改的具体方法。
  4. 加深对于业务代码的理解,在单元测试的时候,需要明确业务方法的功能和职责单一性,因此编写单元测试用例时,可以不断的review、重构某个业务代码的实现。

单元测试划分

根据测试目录分类:

Android Studio 中的典型项目包含两个用于放置测试的目录。请按以下方式组织整理您的测试:

  1. androidTest 目录应包含在真实或虚拟设备上运行的测试。此类测试包括集成测试、端到端测试,以及仅靠 JVM 无法完成应用功能验证的其他测试。
  2. test 目录应包含在JVM本地计算机上运行的测试,如单元测试。

test目录下运行在JVM本地环境,运行速度较快,但是没发全面测试依赖于Android环境的方法,但是使用Robolectric可以模拟部分Android环境。

androidTest运行在虚拟设备、真实设备上,可以模拟Android运行环境,但是运行速度较慢,可提供最高的保真度。

按照运行环境划分

  1. JVM本地运行环境
  2. 模拟运行环境(Robolectric)
  3. 真机运行环境、虚拟器运行环境

测试与迭代

  1. 在软件迭代开发新功能时,设计新功能需要编写相应的单元测试程序,单元测试需要包含新功能的所有可能的case,保证最小颗粒度(方法)符合预期的效果。
  2. 在代码重构的时候,需要进行集成、UI测试,根据集成、UI测试的结果,持续迭代代码。

与由测试驱动的迭代开发关联的两个周期。图片来自官网:

在这里插入图片描述

测试金字塔

测试金字塔说明了应用应如何包含三类测试(即小型、中型和大型测试):

小型测试是指单元测试,用于验证应用的行为,一次验证一个类或者一个方法。

中型测试是指集成测试,用于验证模块内部的逻辑单元,没有涉及到多模块之间的数据交互。

大型测试是指端到端测试,用于验证跨越了应用的多个模块的用户操作流程。

沿着金字塔逐级向上,从小型测试到大型测试,各类测试的保真度逐级提高,但维护和调试工作所需的执行时间和工作量也逐级增加。因此,您编写的单元测试应多于集成测试,集成测试应多于端到端测试。虽然各类测试的比例可能会因应用的用例不同而异,但我们通常建议各类测试所占比例如下:小型测试占 70%,中型测试占 20%,大型测试占 10%。

在这里插入图片描述

单元测试Demo

google官方资料:

https://developer.android.com/training/testing/fundamentals?hl=zh-cn

Android的测试是基于junit。 从测试运行环境分成:基于JVM进行本地的单元测试(Local unit tests), 基于Android设备进行设备化的测试(instrumented tests)。

下面主要介绍基于JVM的本地单元测试:

(1)创建单元测试package

一般在 module-name/src/包目录下会有test/java/包名,我们可以在test/java下编写单元测试代码。(一般情况下新建android项目时,该目录已经存在,不需要单独新建)


在这里插入图片描述

Junit常见API

在JUnit框架测试中,你可以安装(初始化操作), 卸载(释放资源), 和断言操作。测试方法需要由@Test注解,目的是为了测试我们目标代码能否按照我们预期输出正确结果。

class ExampleUnitTest {
    @Before
    fun setUp(){

    }
    @Test
    fun addition_isCorrect() {
        assertEquals(4, 2 + 2)
    }

}
  1. @Before: 使用这个注解可以指定一段包含测试设置操作的代码。测试类在每个测试执行前调用这段代码。你可以定义多个带有@Before注解的方法, 但是在测试类中这个方法执行的顺序是不确的。
  2. @After: 这个注解指定一段代码包含测试卸载操作。在每个测试方法执行后调用这段代码。你可以定义多个@After操作的代码。使用这个注解来释放内存中的资源。
  3. @Test: 使用这个注解标识一个测试方法。 一个单独的测试类可以有多个测试方法
  4. @Rule: @Rule通过复用的方法,可以允许你可以灵活的添加和修改每个测试方法。 在Android测试中, 此注解需要和Android测试支持库中提供的测试规则
  5. @BeforeClass: 使用此注解来指定一个测试类的静态方法只能调用一次。这种测试步骤对于耗时操作非常有用, 例如连接数据库操作。
  6. @AfterClass: 使用这个注解来指定一个静态方法, 当类中所有的测试方法都已经运行完成的时候调用。 这个步骤对释放@BeforeClass块中占用的资源非常有用。
  7. @Test(timeout=): 一些注解支持在注解中设置变量值。 例如, 你可以指定一个测试的超时时间,如果一个测试开始并且没有在指定的超时时间内完成, 它自动认为校验失败。超时时间的单位是毫秒, 如 @Test(timeout=5000)

下面举例说明:

获取三个数中最大的那个数:

public class MathUtil {
   public static int max(int a, int b, int c){
       if(a > b){
           if(a > c){
               return a;
           }else{
               return c;
           }
        }else{
           if(b > c){
               return b;
           }else{
               return c;
           }
        }
    }
}

编写的单元测试代码如下:

public class MathUtilTest {
    @Test
    public void testMax() {
          c(3, MathUtil.max(1, 2, 3));
    }
}

MathUtilTest就是一个测试类,testMax方法就是一个测试方法,assertEquals是junit4中提供的一个断言方法,如果MathUtil.max的输出和3是一致的,那么该单元测试pass。

junit的断言可以使用truth库来替换。

testImplementation 'com.google.truth:truth:1.0.1'

运行单元测试代码

(1)运行单个测试方法:

找到我们需要运行的测试类,例如:MathUtilTest,点击被@Test注解的方法即可。


在这里插入图片描述

(2)运行某个测试类:


在这里插入图片描述

(3)运行整个测试模块:
在这里插入图片描述

以上方式的运行结果,可以直接在AS的控制面板查看:


在这里插入图片描述

(4)gradle命名:
./gradlew testDebugUnitTest
在这里插入图片描述

(5)复制运行结果到浏览器,就可以查看最终结果了:

在这里插入图片描述

参考文档:https://developer.android.com/training/testing/fundamentals?hl=zh-cn

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271