unit testing/ TDD有用的devise模式?
阅读这个问题帮助我巩固了unit testing中一直存在的一些问题,TDD等。
从TDD发展的方法来看,我知道这是正确的道路。 阅读各种教程帮助我理解如何开始,但是他们一直是非常简单化的 – 不是真的可以应用于活跃的项目。 我所pipe理的最好的方法是围绕我的代码的一小部分编写testing,比如图书馆,主应用程序使用这些东西,但没有以任何方式进行集成。 虽然这是有用的,它相当于大约5%的代码库。 关于如何进入下一步,还有很less的东西,以帮助我在主应用程序中进行一些testing。
诸如“ 没有unit testing的大多数代码都是用很强的依赖性(即新的遍布在这个地方)或者静态方法来构build的 ”和“ ……类之间具有高级耦合并不难得,难以configuration在你的class级内的对象等等。 “让我意识到下一步是理解如何分离代码以使其可testing。
我应该看什么来帮助我做到这一点? 是否有一套特定的devise模式,我需要了解并开始实施,这将使testing更容易?
Mike Clifton在2004年描述了24个testing模式。在deviseunit testing时,它是一个有用的启发式。
http://www.codeproject.com/Articles/5772/Advanced-Unit-Test-Part-V-Unit-Test-Patterns
通过/失败模式
这些模式是你的第一道防线(或攻击,取决于你的观点),以保证良好的代码。 但是要注意的是,他们告诉你关于代码的内容是欺骗性的。
- 简单的testing模式
- 代码path模式
- 参数范围模式
数据交易模式
数据交易模式是解决数据持久性和沟通问题的一个开始。 有关此主题的更多信息将在“模拟模式”下讨论。 而且,这些模式故意省略压力testing,例如在服务器上加载。 这将在“压力testing模式”下讨论。
- 简单数据I / O模式
- 约束数据模式
- 回滚模式
收集pipe理模式
许多应用程序正在pipe理信息的集合。 尽pipe程序员可以使用各种各样的集合,但validation(并因此certificate)代码正在使用正确的集合非常重要。 这会影响顺序和约束。
- 收集订单模式
- 枚举模式
- 收集 – 约束模式
- 收集索引模式
性能模式
unit testing不仅要关心function,还要关注forms。 待测代码执行其function的效率如何? 多快? 它使用多less内存? 它是否有效的数据检索换取数据插入? 它是否正确地释放资源? 这些都是在unit testing的范围之内。 通过在unit testing中包含性能模式,实现者可以达到目标,从而获得更好的代码,更好的应用程序和更快乐的客户。
- 性能testing模式
过程模式
unit testing的目的是testing单元…应用程序的基本function。 可以说,testing过程应该被降级到验收testing程序,但是我并不认同这个观点。 一个过程只是一个不同types的单位。 使用unit testing程序进行testing可以提供与其他unit testing相同的优势 – 它logging了过程的工作方式,unit testing人员可以通过按顺序testing过程来快速识别潜在的用户界面问题,从而帮助实施人员好。 术语“过程”还包括状态转换和业务规则,两者都必须经过validation。
- 过程序列模式
- 进程状态模式
- stream程规则模式
仿真模式
数据交易难以testing,因为它们通常需要预设configuration,开放连接和/或在线设备(仅举几例)。 模拟对象可以通过模拟数据库,Web服务,用户事件,连接和/或与代码进行交易的硬件来救援。 模拟对象也有能力创build在现实世界中难以再现的故障条件 – 有损连接,慢速服务器,故障networking集线器等。
- 模拟对象模式
- 服务模拟模式
- 比特错误模拟模式
- 组件仿真模式
multithreading模式
unit testingmultithreading应用程序可能是最难的事情之一,因为你必须build立一个条件,它的本质是asynchronous的,因此是非确定性的。 这个主题本身可能是一篇重要的文章,所以我只在这里提供一个非常通用的模式。 此外,要正确执行多个线程testing,unit testing器应用程序必须自己执行testing作为单独的线程,以便当一个线程结束处于等待状态时unit testing器不被禁用
- 信号模式
- 死锁决议模式
压力testing模式
大多数应用程序都在理想的环境中进行testing – 程序员使用小型数据集,使用networkingstream量小的快速机器。 现实世界是非常不同的。 在某些事情完全中断之前,应用程序可能会遭受降级,并且对用户的响应不佳或出错。 validation代码在压力下的性能的unit testing应该在理想的环境中以相同的热情(如果不是更多)满足unit testing。
- 大容量数据压力testing模式
- 资源压力testing模式
- 加载testing模式
表示层模式
unit testing最具挑战性的一个方面就是validation信息是在表示层本身获得用户权限,而应用程序的内部工作正确地设置表示层状态。 表示层通常与业务对象,数据对象和控制逻辑纠缠在一起。 如果您打算对表示层进行unit testing,则必须认识到必须清楚地区分关注点。 部分解决scheme涉及开发适当的模型 – 视图 – 控制器(MVC)体系结构。 在使用表示层时,MVC体系结构提供了开发良好devise实践的方法。 但是,它很容易被滥用。 需要一定的纪律来确保你实际上正确地实现了MVC体系结构,而不仅仅是一个字。
- 视图状态testing图案
- 模型 – 状态testing模式
我会说你需要主要有两件事来testing,他们齐头并进:
- 接口,接口,接口
- dependency injection; 这与接口一起将帮助您随意交换部件以隔离要testing的模块。 你想testing你的类似cron系统发送通知给其他服务? 实例化它,并用真正的代码实现来代替所有其他的组件,服从正确的接口,但硬连线以你想testing的方式作出反应:邮件通知? 通过抛出exception来testing当smtp服务器closures时会发生什么
我自己还没有掌握unit testing的技巧(而且我远离它),但这是我目前正在进行的主要工作。 问题是,我仍然没有devisetesting,因此我的代码不得不向后弯曲以适应…
迈克尔·菲瑟(Michael Feather)的着作“使用遗留代码有效地工作”正是你正在寻找的。 他将遗留代码定义为“没有testing的代码”,并讨论如何使其得到testing。
就像大多数事情一样, 当您进行更改或修复时,请尝试增加testing覆盖率。 随着时间的推移,你将有一个更完整的testing。 它讨论了减less耦合以及如何在应用程序逻辑之间进行testing的技巧。
正如在其他答案中指出的那样,dependency injection是编写可testing(和通常松散耦合)代码的好方法。
Gerard Meszaros的xUnittesting模式:重构testing代码充满了unit testing的模式。 我知道你正在寻找TDD的模式,但是我认为你会在本书中find很多有用的材料
这本书是在safari,所以你可以得到一个很好的看看里面是什么可能是有帮助的: http : //my.safaribooksonline.com/9780131495050
devise模式与TDD并不直接相关,因为它们是实现细节。 您不应该尝试仅仅因为它们存在而将模式放入代码中,而是倾向于随代码的演变而出现。 如果你的代码很臭,它们也会变得有用,因为它们有助于解决这些问题。 不要在devise模式中开发代码,只需编写代码。 然后让testing通过,然后重构。
让我意识到下一步是理解如何分离代码以使其可testing。
我应该看什么来帮助我做到这一点? 是否有一套特定的devise模式,我需要了解并开始实施,这将使testing更容易?
对! 固体是你在找什么(是的,真的)。 我一直在推荐这两本电子书 ,特别是针对这个问题的SOLID。
如果您要将unit testing引入现有的代码库,您还必须了解它非常困难。 不幸的是,紧密耦合的代码太常见了。 这并不意味着不这样做,但是一段好时间就像你提到的那样,testing将会更集中在小块。
随着时间的推移,这些问题会变成一个更大的范围,但这取决于现有代码库的大小,团队的规模以及实际上有多less人正在做这件事,而不是增加问题。
很多像这样的问题都可以通过适当的封装解决。 或者,如果您正在混合您的疑虑,则可能会遇到此问题。 假设你有validation用户的代码,validation域对象,然后将域对象全部保存在一个方法或类中。 你混淆了你的担心,你不会很高兴。 您需要分离这些问题(authentication/授权,业务逻辑,持久性),以便您可以单独testing它们。
devise模式是有帮助的,但是很多外来的devise模式有很窄的问题,可以应用。 像复合,命令这样的模式经常被使用,而且很简单。
指导原则是:如果很难testing某些东西,可以将其重构为较小的问题,并单独testing重构的位。 所以,如果你有一个200行的方法与5级的if语句和几个for循环,你可能想要打破吸盘。
因此,首先看看您是否可以通过分离您的担忧来简化复杂的代码,然后查看是否可以简化复杂的代码。 当然,如果devise模式突然出现在你身上,那就去做吧。
Arrange,Act,Assert是模式的一个很好的例子,可以帮助你围绕特定的用例构buildtesting代码。
这里有一些假设的C#代码演示了这个模式。
[TestFixture] public class TestSomeUseCases() { // Service we want to test private TestableServiceImplementation service; // IoC-injected mock of service that's needed for TestableServiceImplementation private Mock<ISomeService> dependencyMock; public void Arrange() { // Create a mock of auxiliary service dependencyMock = new Mock<ISomeService>(); dependencyMock.Setup(s => s.GetFirstNumber(It.IsAny<int>)).Return(1); // Create a tested service and inject the mock instance service = new TestableServiceImplementation(dependencyMock.Object); } public void Act() { service.ProcessFirstNumber(); } [SetUp] public void Setup() { Arrange(); Act(); } [Test] public void Assert_That_First_Number_Was_Processed() { dependencyMock.Verify(d => d.GetFirstNumber(It.IsAny<int>()), Times.Exactly(1)); } }
如果你有很多场景需要testing,你可以用具体的Arrange&Act位(或者仅仅是Arrange)提取一个通用的抽象类,然后在组合testing函数的inheritance类中实现剩余的抽象位和testing函数。
dependency injection/ IoC。 还阅读了dependency injection框架,如SpringFramework和谷歌guice。 他们也瞄准如何编写可testing的代码。