《Effective Unit Testing》 读书笔记 4 测试替身(Test Doubles)

测试替身就是通过写一些代码来替代所测试代码会接触到的其他部分代码。首先需要回答的问题是我们为什么需要测试替身。作者列举了以下几点:

  • 隔离要测试的代码
  • 加快测试的执行
  • 让执行具有确定性
  • 模拟特殊的条件
  • 访问隐藏的信息

下面我们分别来看这几点。
隔离要测试的代码:这意味着我们要分清楚测试的代码和与测试代码所交互的代码。原始代码写得不好或者一开始就不是面向测试的话,其实很难隔离,现实中这会导致测试代码往往会由于依赖代码的错误,而不是自己代码的错误而通不过。

加快测试的执行:这个比较好理解,通过替身我们实际上不需要去实际执行所依赖的代码的逻辑(其中可能包含复杂的算法,访问其他网络服务,费时的IO访问等等),既然没有实际执行这些代码,测试自然就变快了。

让执行具有确定性:最典型的例子是依赖当前系统时间的代码。需要把取时间这部分代码做成替身。

模拟特殊的条件:比如断网的条件,或者某个外部服务意外报错的情况。这里作者搞笑了一把,说如果通过代码远程遥控乐高机器人把现实中的网线拔了当然是更牛逼。

访问隐藏的信息:一般说来,如果测试代码需要访问类内部实现的话,多半是在类设计上了问题。对java来说意味着要访问一些私有变量之类。但是如果在测试中真发现需要为类添加仅仅为了测试的方法的时候,就该考虑用一个测试替身作为子类继承原来的类,把这些方法加到测试替身这个子类上。

下面谈谈测试替身的类型, 作者分为了四种:

  • Test Stub
  • Fake Object
  • Test Spy
  • Mock Object

我也不知道每个准确的中文翻译是什么,我觉得大部分人英文应该能看懂,但是很可能除了清楚Test Spy(测试间谍)功能比较不同外,大部分人不清楚其他三者的分别。下面就来解释一下。

Test Stub:Stub这个词是指短短的一截东西,比如木桩,烟蒂。所以Test Stub的特点就是短。作者举了个很好的例子,比如我们的代码用到了一个打Log的库,Log的实际实现可能很复杂,需要把Log推送到某个远程服务器或者存到数据库,我们手头的测试并不关心Log的输出,只求测试代码跑到需要打Log的地方不出错即可,我们就可以通过Test Stub把Log的接口实现了,每个接口只需要留空就行。见下面的例子,log方法留空即可。

   public class LoggerStub implements Logger {
       public void log(LogLevel level, String message) {
        // still a no-op
      }
      public LogLevel getLogLevel() {
        return LogLevel.WARN; // hard-coded return value
      }
}

如果Test Stub对于方法的简单实现(通常只有一行return代码)不能满足需求,就要用到下面的Fake Object了

Fake Object:假对象。假对象的典型例子是模拟数据访问层。通过模拟数据访问层我们可以不访问数据库,而实现简单的增删改操作,把对象存在内存中。比如大家可以思考怎么通过维护一个内部数组来模拟下面的接口。

public interface UserRepository {
    void save(User user);
    User findById(long id);
    User findByUsername(String username);
}

Test Spy:大部分测试的代码,我们可以通过对这段代码的输入输出来判断代码的正确性。但在某些情况下这是无法实现的。比如说,我们要测试的东西并不能通过输出来判断因为执行的代码根本没有任何输出,我们要测试的是在某种情况下代码有没有调用某个特定接口之类。这时候需要为这些接口上建立Test Spy来记录接口方法是否被执行,传入接口的参数是否正确。
比如下面的对于Log的Test Spy,里面的received方法就是用来在事后检测特定的log是否被write方法记录下来了。

private class SpyTarget implements DLogTarget {
    private List<String> log = new ArrayList<String>();
    @Override
    public void write(Level level, String message) {
        log.add(concatenated(level, message));
    }
    boolean received(Level level, String message) {
        return log.contains(concatenated(level, message));
    }
    private String concatenated(Level level, String message) {
        return level.getName() + ": " + message;
    }
}

Mock Object:这是一种加强版的Stub,在所替代的方法上,规定当接收到特定的参数时候表现出指定的行为。同时又可以说是一种简化版的test spy,因为基本只是记录方法的入参和调用的次数,并不记录其他内部状态。这时候我们可以选择库而不是自己实现,对Java而言,如JMock,Mockito,EasyMock。实际上以上四种替身类型,除了Fake Object可能不太合适外,基本都可以用mock的库来实现。

下一篇文章将介绍如何用好测试替身。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 2. 单元测试进阶——寻求优秀 2.1 使用测试替身 在现代开发者测试的上下文中,除了允许在某些依赖缺失的情况下编...
    厲铆兄阅读 731评论 0 2
  • 单元测试实践背景 测试环境定位bug时,需要测试同学协助手动发起相关业务URL请求,开发进行远程调试问题:1、远程...
    Zeng_小洲阅读 7,611评论 0 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,533评论 18 399
  • 赛车、篮球、短裤及歌剧:认识自己的短处,突破制约 你一定要知道的 无论是身体的、心理的还是社会强加给我们的制约,我...
    peony007阅读 139评论 0 0