爱悠闲 > Android开发使用MockObject进行测试代码

Android开发使用MockObject进行测试代码

标签: Android,测试  |  作者: edhroyal 相关  |  发布日期 : 2015-01-30  |  热度 : 959°
最近写测试感觉有些想法,于是决定写下来,欢迎探讨。

为什么要写测试?
写软件要保证质量,自动测试是个好的东西。特别是现在的软件开发并不是一发布就完了,更多的是采取跌代开发的方式:先发布一个功能相对简单的版本,然后根据市场反映在此基础上不断的升级更新。这种时候,自动测试尤其有用。

试想你改动一个东西,如果没有一个东西来保证你的改动不会破坏其他功能,可能你自己心里也没底的吧。你当然可以每次手动的去检查重要的部分,那细节的部分呢?而且,特别是有时候你改些看起来完全不相关的东西,但还是会影响到。手动测试一个是慢,二是通常最后才来做手动测试, 如果一个问题到了最后关头才被我们发现,那一定是一场灾难。如果你每改动几个小的地方,然后花几秒种就可以让测试跑一遍,那就可以极早的发现和解决问题。一个问题越早发现和处理,它花的成本就越低。

自动测试虽然不是万能的,但是如果我们能把测试写的足够好,就可以帮我们尽早的发现和避免大部分问题。

就我自身来说,实际写测试的时候并不多,而且我一直觉得测试是个很没必要的东西:只要你的思路正确,那就应该没问题嘛。但是有一次我就在改一个bug的时候就切身感受到了自动测试的好处。因为我改动的功能涉及到的细节比较多,于是,每次改点东西我总是需要手动的去测试。做到一半的时候,我加入了单元测试,这时,我就再也不用每次改一点都手动测试一下了,而且我对自己的代码完成的功能也更有信心了:因为有代码帮我做自动检查(当然最后做完了后,手动测试一下还是必要的)。

什么时候写测试
总体感觉的话,在合适的时机加入测试会提高开发的效率和代码的健壮性。但什么时候算是合适的时候呢?

我在重构一书中看到过这么一个观点,我一直觉得很受用:在开发的过程中只有两个状态:一是开发状态,只管开发功能,不管代码有多丑,结构多么奇怪;二是重构状态,只管改善代码的结构,决不添加新功能。我觉得如果是以这种方式来开发代码的话,感觉在重构后添加测试代码是比较合适的时机。因为过早的添加测试代码的话,重构过后有可能需要大量的改进测试代码,是一种不小的负担。但是在重构过后,短期内代码大幅改动的可能性就小多了。

那么如果开始的时候,代码的结构就已经设计好、限定死了,那一开始加入测试似乎就是不错的想法,这也许就是测试驱动开发的应用场景吧。我没有经历过测试驱动开发的实践,只好做一些猜测。

怎么写测试
简单点的测试我想大家应该都会写。但是做Android开发的时候,我遇到了很多不好测试的情况,就决定仔细研究下官方的文档。因为我想,Google在做Android的时候,肯定考虑到了这个问题的。
先给链接,有兴趣的可以自己看(其实很多做Android的人估计都看过,但是认真看过的估计就少了):

我主要想说的是关于 MockObject的使用,这也是我最近写测试的一些心得。

基本思路
很多时候,单元测试还是比较好写的。但是如果一段代码受到运行时环境的影响,那就比较麻烦了。认真研究了Andriod的测试框架,我发现我原来觉得写测试困难,主要原因在于我没想过把测试的环境独立出来,如果 通过一些手段把测试的运行环境独立起来,自己可以控制运行环境,那么,写测试的难度就大大降低了。 模拟对象(Mock Object)就是干这个用的。

在认真研究了Android的测试框架后,回头看我原来写的测试,才发现原来写的测试问题所在。原来写测试的思路主要是尝试去改变运行环境来达到测试目的,这样不是不行,但是弊端相当的明显:首先,你改变了运行环境,可能会对环境造成意外的破坏;其次,有时候要改变环境是件困难,甚至不可能的事情,系统中有些东西是受到保护,无法修改的。还有可能会有些我暂时无法想到的坏处。

如果通过Mock Object来独立一个单元的运行环境的话,这种情况就改善了许多。第一,运行环境切实的掌握在了自己的手中。自己可以根据情况让模拟对象按照期望的行为来运作,可以方便的测试一些极端情况。

实际操作
实际怎么做呢?

首先,我们要想到,我们的代码中涉及到环境的影响是有限的,这就给我们模拟环境(主要是context)提供了可能性。如果你发现你的一段代码受到环境影响太大,可以看看你的设计思路是否有问题,或者,还是本身必须这么干。

MockObject具体的使用方法就是继承相应的模拟对象,比如MockContext,那么就继承它,然后重写你需要用到的方法,然后把这个模拟的context的实例传给需要的测试。这样,就可以将你的测试和真正的运行环境隔离开来,保证了测试的独立性,而且提高的测试的可控性。
例如:
class FakeContext extends MockContext {
      @override
      public File getCacheDir(){
            return somefile;
      }
}

在对某个MockObject的行为不太了解的时候,可以看看官方文档或源码,现在获取源码是很方便的事情。

关于实际操作我写的很少,因为我觉得,在思路正确了以后,稍微看看源码,做下实践,并不是什么困难的事情。关键就在于实践,实际做一做,比看多少书都来的有效。