单元测试/ TDD的有用设计模式?

Phi*_*ham 48 testing tdd unit-testing design-patterns

阅读这个问题有助于我巩固我在单元测试,TDD等方面遇到的一些问题.

由于遇到了TDD开发方法,我知道这是正确的发展方向.阅读各种教程帮助我了解如何开始,但它们一直非常简单 - 不是真正可以应用于活动项目的东西.我所管理的最好的是围绕我的代码的一小部分编写测试 - 像库这样的东西,主应用程序使用但没有以任何方式集成.虽然这很有用,但相当于约5%的代码库.关于如何进入下一步,帮助我对主应用程序进行一些测试的问题很少.

诸如" 大多数没有单元测试的代码都是用硬依赖(即全新的)或静态方法构建的. "和" ......在类之间具有高级别的耦合,难以配置的情况并不罕见你班级内的对象等等. "让我意识到下一步是理解如何解码代码以使其可测试.

我该怎么做才能帮助我做到这一点?是否有一组特定的设计模式需要我们理解并开始实施,这样可以更轻松地进行测试?

Dav*_*ave 56

Mike Clifton描述了2004年的24种测试模式.在设计单元测试时,它是一种有用的启发式方法.

http://www.codeproject.com/Articles/5772/Advanced-Unit-Test-Part-V-Unit-Test-Patterns

通过/失败模式

这些模式是您的第一道防线(或攻击,取决于您的观点),以保证良好的代码.但是要注意,他们在告诉你有关代码的内容方面具有欺骗性.

  • 简单测试模式
  • 代码路径模式
  • 参数范围模式

数据交易模式

数据事务模式是接受数据持久性和通信问题的开始.有关此主题的更多信息,请参见"模拟模式".此外,这些模式故意省略压力测试,例如,在服务器上加载.这将在"压力测试模式"下讨论.

  • 简单数据I/O模式
  • 约束数据模式
  • 回滚模式

集合管理模式

许多应用程序所做的是管理信息集合.虽然程序员可以使用各种集合,但验证(并因此记录)代码使用正确的集合非常重要.这会影响排序和约束.

  • 收集订单模式
  • 枚举模式
  • 集合约束模式
  • 集合索引模式

表现模式

单元测试不仅要关注功能,还要关注形式.被测代码如何有效地执行其功能?多快?它使用了多少内存?它是否有效地折衷数据检索以进行数据检索?它能正确释放资源吗?这些都属于单元测试的范畴.通过在单元测试中包含性能模式,实现者可以达到目标,从而产生更好的代码,更好的应用程序和更快乐的客户.

  • 性能测试模式

流程模式

单元测试旨在测试单元......应用程序的基本功能.可以说测试过程应该降级到验收测试程序,但是我并没有考虑到这个论点.流程只是一种不同类型的单元.使用单元测试器的测试过程提供与其他单元测试相同的优势 - 它记录了过程的工作方式,单元测试人员可以通过不按顺序测试过程来帮助实现者,快速识别潜在的用户界面问题,如好.术语"进程"还包括状态转换和业务规则,两者都必须经过验证.

  • 过程序列模式
  • 流程状态模式
  • 流程规则模式

模拟模式

数据事务很难测试,因为它们通常需要预设配置,开放连接和/或在线设备(仅举几例).模拟对象可以通过模拟与代码进行交易的数据库,Web服务,用户事件,连接和/或硬件来解决.模拟对象还能够创建在现实世界中很难再现的故障条件 - 有损连接,慢速服务器,故障网络集线器等.

  • 模拟对象模式
  • 服务模拟模式
  • 误码模拟模式
  • 组件模拟模式

多线程模式

单元测试多线程应用程序可能是最困难的事情之一,因为你必须设置一个条件,它本质上是异步的,因此是非确定性的.这个主题本身可能是一篇重要的文章,所以我在这里只提供一个非常通用的模式.此外,为了正确执行许多线程测试,单元测试器应用程序本身必须将测试作为单独的线程执行,以便在一个线程最终处于等待状态时不禁用单元测试器

  • 信号模式
  • 死锁解决模式

压力测试模式

大多数应用程序都在理想的环境中进行测试 - 程序员使用的是一台使用小型数据集的网络流量很小的快速机器.现实世界是非常不同的.在完全中断之前,应用程序可能会遭受降级并且对用户的响应很差或有错误.在理想环境中,单元测试应该在相同的情况下(即使不是更多)满足单元测试,以验证代码在压力下的性能.

  • 批量数据压力测试模式
  • 资源压力测试模式
  • 加载测试模式

表示层模式

单元测试中最具挑战性的方面之一是验证信息是在表示层本身到达用户并且应用程序的内部工作正确地设置表示层状态.通常,表示层与业务对象,数据对象和控制逻辑纠缠在一起.如果您计划对表示层进行单元测试,则必须意识到必须清楚地分离关注点.部分解决方案涉及开发适当的模型 - 视图 - 控制器(MVC)架构.MVC体系结构提供了在使用表示层时开发良好设计实践的方法.但是,它很容易被滥用.事实上,需要一定的纪律来确保您正确地实现MVC架构,而不仅仅是单词.

  • 视图状态测试模式
  • 模型状态测试模式


sam*_*amy 11

我要说你需要主要测试两件事,它们是相辅相成的:

  • 接口,接口,接口
  • 依赖注入; 这与接口一起将帮助您随意交换部件以隔离您要测试的模块.您想测试向其他服务发送通知的类似cron的系统吗?实现它并用你的实际代码实现替换其他所有组件,这些组件遵循正确的接口,但硬连线以你想要测试的方式做出反应:邮件通知?测试当smtp服务器因抛出异常而关闭时会发生什么

我自己还没有掌握单元测试的艺术(我离它很远),但这是我目前的主要努力.问题是我仍然没有设计测试,因此我的代码必须向后弯曲以适应...


Pau*_*bel 7

Michael Feather的书"有效地使用遗留代码"正是您所需要的.他将遗留代码定义为"没有测试的代码",并讨论如何使其受到测试.

和大多数事情一样,这是一步一步.当您进行更改或修复时尝试增加测试覆盖率.随着时间的推移,您将拥有更完整的测试集.它讨论了减少耦合以及如何在应用程序逻辑之间调整测试件的技术.

如其他答案中所述,依赖注入是编写可测试(和通常松散耦合)代码的一种好方法.


Mar*_*ine 5

Gerard Meszaros 的 xUnit 测试模式:重构测试代码充满了单元测试的模式。我知道您正在寻找 TDD 模式,但我认为您会在本书中找到很多有用的材料

这本书是关于 safari 的,所以你可以仔细看看里面的内容,看看它是否有帮助: http://my.safaribooksonline.com/9780131495050


Art*_*Art 5

排列,执行,断言是一种很好的模式示例,可以帮助您围绕特定用例构建测试代码。

以下是一些假设的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));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要测试的场景很多,则可以提取带有具体Arrange&Act位(或仅仅是Arrange)的通用抽象类,并在对测试功能进行分组的继承类中实现其余的抽象位和测试功能。