Android单元测试之JUnit4

单元测试是什么

首先我们来介绍一下什么是单元测试?可能有很多人经常会听到这个词并不感到陌生,那什么是单元测试呢,在Android中又是如何实践的呢,这个时候可能会感到困惑。从名字上看,单元测试就是参与项目开发的工程师在项目中为了测试某一个代码单元而写的测试代码,用于执行项目中的目标函数并验证其逻辑状态或者结果。。这里提到的“一个代码单元”指的是测试的最小模块,通常指函数。这些代码是白盒测试,能够检测目标代码的准确性和可靠性,在打包时单元测试的代码并不会被编译进入release apk中。

为什么要写单元测试

在敏捷开发开发中,Android项目版本快速迭代上线,这个往往需要除黑盒测试之外更加可靠的质量保证,这正是单元测试的用武之地。或许我们经常遇到这种情况,自己的模块代码早早写好了,但其他模块接口还未OK,在最后的紧急关头集成联调时,结果自己的模块出现很多bug,可能连代码最基本的逻辑都跑不通。这个时候就需要单元测试来提前暴露自己的问题,从而保证自己代码逻辑的准确性和可靠性。看到这里,你可以明白单元测试有多重要了:

  1. 对自己的代码更加有信心
    自己写的代码要准备上线的时候,可能感觉会很心虚,总有一种铤而走险的感觉,甚至在上线时祈祷自己的模块没有问题,这些都是对自己写的代码没有信心的表现,毕竟空口无凭。虽然说单元测试并不能百分之百保证在集成联调时能够完全正确运行,但保证了自己的每一代码单元测试通过,可以很明显的增加对代码的信心。
  2. 为代码重构保驾护航
    项目有多人经手,代码很乱很差劲,有时候想去重构,但是又担心重构之后出问题,那该怎么办呢?但是有了单元测试那就不一样,我们可以边重构边写单元测试,这样基本上可以保证我们的重构没有破坏原有的代码逻辑的准确性。比如我现在所在的项目是通过MVC设计模式,所以导致Activity中代码特别臃肿,比较难维护。再者通过MVP设计模式来重构项目代码,可以抽出Presenter这个纯Java层,便于写单元测试。
  3. 写单元测试本身就是一次小范围的代码重构
    好的东西都是千锤百炼出来的,比如好的架构也不例外,都是经历作者不停的review和修改。写单元测试的过程本身也是一个对自我code 不断review的过程,在这个过程中,可以发现一些逻辑设计上的问题,代码编写方面的问题,比如一些边界条件处理、空对象的保护和类构造时参数的依赖问题,还有一些对Android Context的依赖问题。
  4. 节约开发时间
    如果没有单元测试的话,我们coding时为了调试某个功能,看界面是否显示正确,把APP运行起来,如果有错的话,改一点东西,再运行起来。。。哇,这个过程太漫长太痛苦,特别在大型项目中,run一次需要五六分钟,这个时间太漫长了,完全在浪费时间,coding的效率也很低。有了单元测试,在开发过程中可以更少的把APP运行起来,一两分钟的等待就可以验证代码逻辑是否正确,速度相对来说快很多。此外,通过单元测试能帮我们减少bug,从而减少了调试bug和fix bug的时间
  5. 更快的发现bug
    如果没有单元测试,我们就需要在最后的集成联调中才能发现自己模块的问题,一旦问题比较严重的话,可能会让自己感觉有压力和恐慌。但是通过单元测试,可以有多一层的保证,可以更早的发现问题和解决问题,在集成时让自己更加闲庭信步。

使用JUnit4写单元测试

JUnit4是一个Java语言的单元测试框架,由Kent Beck和Erich Gamma编写的一个回归测试框架。多数Java的开发环境都已经集成了JUnit作为单元测试的工具,也是用的最多的一个测试框架,Android Studio创建的工程中就已经集成了JUnit4,默认就加了这个dependencies,如下:

dependencies {
    testCompile "junit:junit:4.12"
}

下面我们来写一个简单的单元测试,假如我们有一个这样的类,定义如下:

public class Calculator {

     public int add(int one, int another) {
        return one + another;
    }

}

我们想测试这个Calculator类的add(int one, int another)方法,在Android studio project中,源代码默认放在src/main/java下面的,而对应的单元测试代码则是放在src/test/java目录中,定义一个CalculatorTest类,单元测试代码如下:

public class CalculatorTest {

    @Test
    public void testAdd() throws Exception {
        Calculator calculator = new Calculator();
        int sum = calculator.add(1, 2);
        assertEquals(3, sum);
    }

}

我们也可以通过Android Studio的快捷方式来创建单元测试类,选中目标类或者目标方法点击右键,选择GoTo--->Test来快速创建单元测试方法,方式如图:

234ABDC2-33D4-406C-92FB-91E1F3BB804A.png

打开CalculatorTest,鼠标右键点击testAdd()方法,选择Run testAdd(),如下图所示:

Paste_Image.png

这里的CalculatorTest是Calculator对应的测试类,这里的testAdd()就是add()这个方法对应的测试方法。所以在单元测试时,就是给你目标类的public方法写对应的测试方法。
一般来说,一个方法对应的测试方法主要有三步曲,以上面的测试方法为例:

  1. setup,一般是new出你要测试的目标类,以及设计一些前提条件:Calculator calculator = new Calculator();
  1. 执行操作,一般是调用我们要测试的那个方法,同时获得结果:int sum = calculator.add(1, 2);
  2. 验证结果,通过JUnit4提供的Asset断言来验证结果是否与预期一样:Asset.assertEquals(3, sum);

在上面的例子中,你是否注意到一个@Test注解,通过声明这个注解,告诉JUnit我们要测的方法。JUnit4使用Java5中的注解(annotation),以下是JUnit4常用的几个annotation:

@Before:初始化方法 对于每一个测试方法都要执行一次(注意与BeforeClass区别,后者是对于所有方法执行一次)
@After:释放资源 对于每一个测试方法都要执行一次(注意与AfterClass区别,后者是对于所有方法执行一次)
@Test:测试方法,在这里可以测试期望异常和超时时间
@Test(expected=ArithmeticException.class)检查被测方法是否抛出ArithmeticException异常
@Ignore:忽略的测试方法
@BeforeClass:针对所有测试,只执行一次,且必须为static void
@AfterClass:针对所有测试,只执行一次,且必须为static void

一个JUnit4的单元测试用例执行顺序为:
@BeforeClass -> @Before -> @Test -> @After -> @AfterClass;
每一个测试方法的调用顺序为:
@Before -> @Test -> @After;

小结

单元测试不是集成测试,单元测试只是测试一个方法单元,不是测试一整个流程。集成测试是一种end to end的系统测试,测试相关模块集成在一起是否能够按照预期工作,一般都是接口或者功能层面的测试,可能会依赖很多系统因素,测试的代码逻辑一般比较复杂,运行时间会比较长,出错之后的修复成本高。
单元测试的目标函数主要有三种:

  1. 有明确的返回值,如上图的add(int one, int another),做单元测试时,只需调用这个函数,验证其返回值是否符合预期结果。
  2. 这个函数只改变其对象内部的一些属性或者状态,函数本身没有返回值,就验证它所改变的属性和状态。
  3. 一些函数没有返回值,也没有直接改变哪个值的状态,这就需要验证其行为,比如点击事件。

感谢您对本篇博客的关注,有什么不足请指正!要是想深入了解单元测试,了解Mockito测试框架,请关注博客Android单元测试之Mockito!

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,497评论 25 707
  • 单元测试 单元测试是一件繁琐但又不能不做的事。单元测试就是参与项目开发的工程师在项目中为了测试某一个代码单元而写的...
    johnnycmj阅读 770评论 0 0
  • 说起软件测试四个字,想必大家脑海中浮现的有集成测试、系统测试、黑盒测试、白盒测试等,可能就是没想到会有单元测试。 ...
    斜杠时光阅读 3,231评论 0 6
  • 什么是单元测试 在计算机编程中,单元测试(Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最...
    HelloCsl阅读 10,939评论 1 46
  • ——2006年11月8日 胡歌专栏·第二篇 出事至今,灵魂一直在流浪。 猛烈的撞击把我的灵魂从驱壳中抛了出来,...
    hugh_diary阅读 143评论 0 0