“Classic”与“Mockist”TDD,真的对立么? -管理资料

管理资料 时间:2019-01-01 我要投稿
【www.unjs.com - 管理资料】

    这周Yahoo“测试驱动开发(TDD)”小组里面有个热门贴子,是讨论“classic”和“mockist”TDD方式之间的对立统一关系,

“Classic”与“Mockist”TDD,真的对立么?

。Steve Freeman、Nat Pryce、Michael Feathers、Dale Emery等很多人对这些术语进行了讨论,并描述了各自的工作方式。他们也探讨了是否确实存在这种关系。如果存在这种关系,又是什么从本质上区分开这两种方式?

    在声明自己“倾向于classic TDD开发方法”之后,Olaf Bjarnason 在小组里面用问题——“如果你以前采用classic(方式),是什么让你转向[‘mockist’]?觉得新方法怎么样?”——发起了一场超过70条贴子的讨论。然而,前期的回复大多是围绕着Olaf的贴子标题“classic/mockist 讨论”进行的,即是否确有必要进行如此绝对对立的划分。

    JMock 的合作创始人Nat Pryce表达了他的看法:

    我认为把TDD分成“mockist”和“基于状态”是没有意义的,不仅分散了关注点,而且妨碍人们去尝试学习和实践TDD。

    Mock对象只是一种工具,只是实践TDD时会使用到的工具之一。跟其他工具一样,它们被设计出来以帮助解决特定上下文的一系列问题。脱离了上下文,它们不会有任何帮助,甚至成为障碍。

    如果确实如此,那怎么知道该何时使用这种“工具”呢?怎么判断该测试“状态”(不使用mock),还是该验证行为(使用mock)?

    Dale Emery也加入讨论,讲了他一般是怎么区分的:

很多人使用“基于状态”和“基于行为”的说法来区分。我换了种说法,我认为是“结果”和“协议”。如果是测试UUT(unit under test,被测试单元)是否返回了正确的结果,我通常不使用mock。如果是测试UUT是否基于协议扮演了恰当的角色——比如,给定正确的起始条件和影响因素,(UUT)是否向正确的协作者发送了正确消息——我会用mock来代替UUT的直接协作者。

    Lior Friedman认为mock的用途是展示一种契约:

    对我来说,mock指出了被测试覆盖的类和被(该类)调用的其他类之间存在的“契约”。(mock)测试的目的是确保契约被满足了。

    Charlie Poole 就“什么时候使用基于状态(的测试)”提出了他的论断:

    如果没有其他办法来观察对象的行为,也就是说如果方法调用只会引起对象外界可观察状态的改变,我会选择基于状态的测试。而假如事实上对象做了些什么,我会[使用mock]验证它的确是做了。

    Adam Sroka 解释了他何时使用mock:

    就我个人而言,我通常是在系统定义的边界处(比如文件系统、网络、数据库等)使用mock。在自顶而下的开发中,为了得到接口和客户端的松耦合,我通常会使用mock。相反,当交互的对象很小、易于用假对象/stub对象替代时,我一般不使用mock。

    随着讨论的继续,讨论中反复出现这样的现象(可能上文的摘录也体现了这点):虽然不少贴子的观点类似,但是它们并没有使用通用的分类和命名,只是强调“我们有时这么做,其他时候不是”,

管理资料

“Classic”与“Mockist”TDD,真的对立么?》(https://www.unjs.com)。而且,每个贴子(可能除了Sroka“自顶而下”的论断)关注更多的是:到底是设计驱动mock,抑或相反,mock驱动设计?

    关于mock驱动设计,Michael Feathers给出了如下例子:

对我来说,问题的根本在于人们愿意在多大程度上遵循“tell, don’t ask.”mocks支持了这种设计方式。

    ...

    假设你拥有某一类对象,想得到它的错误:

    class Errors {

    int errorCount();

    Error getError(int index);

    }

    Errors errors = object.getErrors();

    这就是ask。我们可以基于状态对Errors对象进行测试。

    为了改成tell,你需要进行如下处理:

    interface ErrorReceiver {

    void accept(Error error);

    }

    ErrorReceiver receiver = ...;

    object.reportErrors(receiver);

    我们可以对mock的receiver对象指定预期行为,使之通过测试,而在生产代码中使用“真实”的类。

    Steve Freeman(JMock的另一位合作开发者)发表了一篇贴子,描述了他的搭档Nat Pryce对一些“值得mock的相关对象组(mock-worthy peer objects)”提出的分类(“Dependencies”,“Notifications”和“Policies”)。在回应的贴子中,Feathers(以及Colin Jack)认为他们两人(Freeman和Pryce)提出的设计理念实际上就是“mock驱动设计”哲学的核心的一部分。在接下来的讨论中,扼要的讲,当人们提到“mockist TDD”,主要是指这些理念。而且,随着这些更具体的名字,之前反复出现的关于“classic与Mockist TDD”的混淆也减少了。

    Pryce提醒小组成员,他和Freeman计划写一本书,提供一些这方面需要的解释。同时他也建议小组成员去参阅他的一篇文章“基于状态与交互的测试”。

    坦率的说,这种讨论并不是什么新货色(Google一下),过一段时间就会出现一次。是否存在着“classic TDD”,而它是否又意味着“设计引出mock”或者其他特性?是否存在“mockist TDD”,体现着“mock引出设计”的哲学?“Tell, Don"t Ask”,这是否又是完全不相干的东西?不管怎样,这是一种“非此即彼”的分类么,又或者“这种适合这些,那种适合那些”的分类更合适?

    当然,一如既往,本文只是摘录了Yahoo小组讨论中的突出部分[希望本文是客观的],仅仅是完整讨论的一部分。你可以自行阅读该贴以及其他相关资料,在这里或讨论贴里与其他人分享你的经验。

    查看英文原文:"Classic" versus "Mockist" TDD, Distinction Real?

    本文出自:http://www.infoq.com/cn/news/2009/02/classic-mockist-tdd

最新文章
推荐文章