支持DHH又一篇文章,认为测试会导致不必要的复杂性,间接性和抽象,一个坏的设计就算是有一套完整的测试也是坏的设计。
Test-induced design damage。
“代码很难单独测试是因为设计不当”,这是TDD格言。隔离意味着与上下文和协作者切断依赖,特别是一些“慢”的东西,如数据库或文件IO。“单元”的普遍定义是单元测试(虽然不是每个人都同意这一点)。
这有时是对的,但是有时很难对坏的设计设置切入点,比如乱糟糟的职责行为或其他什么,这就说明难于单元测试并非一定总是因为设计不够充分,其实,你精心设计的代码,却很难进行单元测试。
这种不幸是因为测试导入的设计影响了流程,所谓测试导入的设计是指当改变你的代码,有如下考虑:要么有利于a)更容易测试先行, b)快速测试,或c )单元测试,但是这样做会损害代码的清晰度。引入了不必要的间接概念。设计被扭曲的变形只是为了适应测试目标。
忠实TDD'ers拒绝这种情况发生的可能。一个真正的信徒是只从好的方面来看待测试。在云计算的审判下, TDD已经从专业编程的救星的位置上跌落到被审判的地步。当前 “ TDD已死”的起义孕育出生了。
我想为什么我们已经能够这么长时间没有去质疑TDD到底是否值得作为设计原则的一部分,其实这是事后合理化而已。如果您接受的前提是红 - 绿 - 重构(red-green-refactor)是所有编程设计的真正的指路明灯,在它的祭坛上的任何牺牲似乎微不足道。谁在乎你需要间接的两个或三个额外的层进行单元测试控制器?当然,这是值得的。
六边形(Hexagonal)设计的损坏
这方面的一个很好的例子是已故的Jim Weirich在Rails的示范六角形结构(https://www.youtube.com/watch?v=tg5RFeSfBM4)。本演示展示了“从Rails解耦”的做法,。
当你观看演示,听取设计理据。他们都是有关测试!这是关于拥有更快的测试,而不触及数据库,它是关于能够测试控制器逻辑却不依赖上下文。
为了实现这一目标,简单的控制器直接禁止访问Active Record的,得通过仓储资源库(Repository)。而Action本身是被挖空的,只是提取一个Command对象,这个Command通过监听模式再回调控制器。
这并不好
该代码为实现两个测试目标已经遭受了巨大的设计损害,测试目标是:更快的测试和易于模拟(easy-to-mock)的单元测试控制器。这是试图在在“应用程序”和Rails之间进行分界,支持者喜欢分开它们两者。
六角形(hexagonal)设计模式本身并不为怪。有可能是一种正确的方向,特别是对于将Web应用领域之外的地方。也许,如果你正在做一个带有常规GUI界面或语音控制或什么的系统,而这些不能只通过一个Web API , Web界面只是众多界面中一个而已。
但是,六边形图案为了实现测试驱动模式的应用被误用为Rails应用中了。
同样使用其他模式一起来实现这一目标。将对ActiveRecord访问隐藏在仓储Repository后面,作为一种与数据库解耦的方式从而实现快速测试目标,它并没有阐明应用程序本身的设计。如果你不关心测试,你就不会看“后面的代码” (访问ActiveRecord的代码) ,并说“好! ” 。
请远离“单元测试是最好的”口头禅,得出这样的一个结论是因为,我认为在Rails(或类似的MVC设置)实现单元测试控制器是一个错误的尝试。控制器的作用是将在一个上下文中对模型的请求与响应结合起来。(banq 注:这是MVC模式对控制器的定义,控制器不能被挪作单元测试用,改变其当初用途)。
控制器目的是要集成测试,而不是单元测试。但是测试金字塔规定,单元级别的测试,其重点是,让人们在默认不知晓的情况下吃进。
我认为对视图进行单元测试是同样愚蠢的,这会将更多关注和兴趣集中在视图,因为视图是MVC这个蛋糕的顶层表面,所以对其相应的测试被比喻为系统测试:端对端。
最后,恐惧模型直接数据库对话的想法已经过时。这种解耦简单的根本不值得一提,现在的Rails作为一个持久存储的控制台程序运行,所以花费1.2秒的开销来引导框架运行单元测试的例子已经一去不复返了。
也许BDD的支持者可能会振振有辞地说,是的,单元测试并不是我们应该做的。我们应该从外而内。但只要这也是在测试这一制度下实施,我认为它通常还是于事无补。它仍然会导致过度Mock和和人工划分边界。
…
最重要的是,不要让你的测试驱动你的设计,而是让你的设计驱动你的测试!要按照MVC模式中规定的正确分层指导你的设计。