使用stub破除external dependency

在测试中,如果因为代码对外部资源存在依赖的行为,尽管代码的逻辑是正确的也会存在测试失败的可能性,这也被称为test-inhibiting

常见的external dependency是系统中的一个对象,被测试的代码需要与这个对象进行交互,但是你并不能去控制这个对象的行为。

stub则是对于系统的external dependency的可控制的替代物。stub带来的效果就是可以在测试代码中无需对external dependency进行直接处理。

提高代码的可测试性

破除dependency最直接的方式就是引入seam,当然这比如是需要refactoring配合的

A型方法:把具体类抽象成接口

抽取接口以便对实现进行替换

B型方法:注入委托和接口(fake implementation)

在被测试类注入stub

在构造函数注入伪对象

利用属性注入的方式注入伪对象

在方法调用前注入问对象

抽取接口方式

public class ExternalManager:IExternalManager

{

public bool IsExternal (string name)

{

......

}

}

public interface IExternalManager

{

bool IsExternal(string name);

}

//测试单元

public bool IsExternalManager(string name)

{

IexternalManager mgr = new ExternalManager();

return mgr.IsexternalManager(name);

}

返回值为true的stub

public class AlwaysTrueExternalManager:IExternalManager

{

public bool IsExternal (string name)

{

return true;

}

}

在构造函数中注入伪对象

这种方式需要给测试类添加新的构造函数或者是个其构造函数添加新的参数,传入抽取出来的接口类型的对象,通过field或var 供被测试方法或其它相关区域的调用。

通过这种方式,测试需要先进行stub的配置,然后传入被测试对象。这种方式可以提高测试代码的可读性,将需要被了解的信息集中在一点地方。

同时,利用构造函数添加参数的方式实现注入,实际上也使得这些参数称为不可选的依赖项,也就是说类的使用者需要为每个所需的特定依赖传入参数。

利用构造函数注入伪对象也可能会带来一些问题,比如拥有多个external dependency 。这时候难道要添加多个构造函数或是添加多个参数?(明显是不明智的选择)

这个问题可以通过创建特殊的类,拥有可以初始化一个类所需要的所有值,这样构造函数就只需要拥有这个类作为唯一参数,当然更好的方式是通过IoC去实现。

属性注入

这个case需要为每个要注入的dependency添加一个属性,包括get和set。然后只需要在被测函数中相应的地方使用这些依赖。 与构造函数的依赖注入方式一样,需要指明必需的和可选的依赖项,因此也会影响到api的设计。通过使用属性,也同时说明了使用这个函数,那些依赖项是不需要的(可能这也是需要使用的属性注入的场景之一)。

方法调用前注入伪对象

这个case很明显的的意思就是我在对这个对象进行操作之前才可以得到其实例,而不是在构造函数或属性中就已经准备好,这个case的不同之处就是发起stub请求的对象就是被测试代码。

这样的话,可以通过使用工厂类来作为实例的提供者,当然必须在构造函数中初始化这个实例提供者。在这个工厂中,可以使用静态方法返回实例了接口的对象实例,同时采用别的方式(如扩展名)返回stub(这种方法会破坏类的设计封装)。

返回stub的层次

第一层:在被测试类中伪造一个成员

添加构造函数,在构造函数中设置类,从测试代码中设置构造参数,还要考虑对api的影响

这种方式会改变被测试类的语义,在没有充分理由情况下一般不采用这种方式。

第二层:在工厂类伪造一个成员

上述方法调用前注入伪对象中提到的就是这一层,把工厂类的属性设置成伪依赖项,这种方式并不会改变语义,一切都是原样,代码也较为简洁。但是使用这种方式,必须要充分了解实例的使用时间。

第三层:伪造工厂类

这种方式需要实现自己专有的伪工厂类,同时这个类也能有或者可能没有接口,若没有,则需要为这个工厂类创建接口,然后再创建伪工厂实例,让其返回依赖项。这种层次可以理解为利用一个伪对象去返回一个伪对象,这个实在是很难想象的事情。

伪造方法

这种方法的层次,不同于上述几种,相比而言,其更接近于被测试代码(一般而言,约接近代码,越少需要更改依赖项)。很重要的一点就是这种方式将被测试的代码也作为一个依赖项。

使用方式:

在测试类中:

添加返回真是实例的虚工厂方法

在代码中正常的使用工厂方法

在测试项目中:

创建新的类

声明这个新类继承被测试类

创建需要替换的接口类型的公共字段(field)

重写虚工厂方法

返回公共字段

测试代码中

创建一个stub类的实例(实现相应接口)

创建新的派生类而非被测试类的实例

配置新实例的公共字段(设置成stub实例)

在测试的派生类中,通过重写工厂方法,产品代码将使用配置的伪对象

这种抽取和重写的方式,可以直接替换依赖项,实现方法避免了大量和接口和虚函数。

这种方式比较适合用于模拟提供给被测试代码的输入,但是不适合验证被测试代码到依赖项的调用。 如果被测试代码是web服务时,得到一个返回值,这是如果想要模拟自己的返回值的话,这种方法就很适合。但是如果想要测试代码对于web服务的调用时是否正确就显得捉襟见肘。 当代码中以及存在可以伪造的接口的时候,有明显的可以使用seam的位置,便不需要使用这种方式。当然如果面对一个密封类,这种方法也显得很无力,无从下手。。。。。

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

推荐阅读更多精彩内容