你真的需要单元测试吗?(2)

发表于:2018-04-16来源:简书作者:戎码半生点击数: 标签:单元测试
),其原因也是我调研并写下本文的原因。Android相对于其他开发环境有以下几个特点: 大多数代码是在与Android环境(SDK)交互,逻辑往往放在后端 UI与逻
),其原因也是我调研并写下本文的原因。Android相对于其他开发环境有以下几个特点:

  1. 大多数代码是在与Android环境(SDK)交互,逻辑往往放在后端
  2. UI与逻辑容易耦合,虽然MVP等模式的出现在尝试缓解这个问题
  3. 版本众多(不同ROM),使用环境复杂(用户行为、网络波动等)导致具体环境的Bug远多于单个模块的Bug

可以想象,当你投入大量精力,使用RobolectricMockito等框架模拟出一个将数据库数据发往后台的单元测试并通过测试用例后,用户却因为切换网络等小概率场景触发了Bug,你会不会感叹我要这单测有何用?类似Android这种终端环境,其边际收益递减的临界点往往更容易达到,引入单元测试犹需谨慎。

你的代码能做单元测试吗

结合上面的分析,哪些场景不适合做单元测试已经显而易见了,When is unit testing inappropriate or unnecessary? [duplicate]中一个高票回答做了如下总结:

  1. The code has no branches is trivial. A getter that returns 0 doesn't need to be tested, and changes will be covered by tests for its consumers.
  1. The code simply passes through into a stable API. I'll assume that the standard library works properly.
  2. The code needs to interact with other deployed systems; then an integration test is called for.
  3. If the test of success/fail is something that is so difficult to quantify as to not be reliably measurable, such as steganography being unnoticeable to humans.
  4. If the test itself is an order of magnitude more difficult to write than the code.
  5. If the code is throw-away or placeholder code. If there's any doubt, test.

Definition of brittle unit tests中也有详细总结,都有一定参考价值。
此外,有了适合单元测试的场景并不代表就有适合单元测试的代码。在TDD模式中,测试先于开发,所以开发部分的代码接口往往需要经过良好的设计和定义,最好能解耦各个模块,如此开发代码将能够完美匹配测试代码。但这种开发模式往往对开发经验、设计能力要求很高。能都达成此境界的已经是TDD的行家了。然而事实是对于没有单元测试经验的开发人员而言,往往没有意识到自己写的代码“不可测试”。以下面伪代码为例:

object processObject(Object object) {
    if (object == objectA) {
        log.i('error 1 ....')
        return object;
    }
    if (object == objectB) {
        log.i('error 1 ....')
        return object;
    }
    .....
    return object;
}

开发人员在Debug的时候,能根据log信息快速定位问题,但对于测试来说就十分不友好了:返回值都一样。如果想要领会单元测试的优越性,短期的镇痛与适应似乎是不可避免的。

写在最后

本文没有讨论TDD的各种优势,也没有讨论单元测试的最佳实践,是个人的一些总结,讨论的是单元测试的一些局限之处,或许有不足、有遗漏,又或者完全错误,欢迎拍砖。譬如,在stackoverflow上有一个关于是否值得做单元测试的问题就因为其争议性而被关闭回答,而又因为有其存在的历史意义而被一直锁定(locked)

原文转自:https://www.jianshu.com/p/1980c944e31c