用简单的例子学习TDD

23

我正在尝试学习测试驱动开发(TDD),但是在如何进行测试方面遇到了困难。我需要编写一个小应用程序,但不知道应该如何测试。

这个应用程序的规格(有些地方简化)如下:

它需要从用户那里得到一个csv文件的位置、一个word文档邮件合并模板的位置和一个输出位置。

然后,该应用程序将读取csv文件,并将每一行数据与word模板合并,输出到指定的文件夹中。

请注意,我并不是在询问如何编写这样的应用程序,因为如果我开始编写,我相信我知道如何去做。但是,如果我想使用TDD来完成它,我需要一些关于测试的指导。我猜想我不想测试读取真正的csv文件,也不想测试第三方组件来进行合并或转换成PDF。

我认为一些常规的TDD指导会很有帮助!

3个回答

34

我建议您考虑每个程序步骤的情况,从失败情况和其预期行为开始:

  • 用户提供空的 csv 文件位置(抛出 ArgumentException)。

  • 用户提供 null 的 csv 文件位置(抛出 ArgumentNullException)。

  • 用户指定的 csv 文件不存在(根据您的想法进行处理)。

接下来,为每种情况编写一个测试,并确保它失败。然后,编写足够的代码使测试通过。对于其中的某些条件来说,这非常容易,因为使测试通过的代码通常就是最终代码:

public class Merger { 
    public void Merge(string csvPath, string templatePath, string outputPath) {
        if (csvPath == null) { throw new ArgumentNullException("csvPath"); }
    }
}

接下来,进入标准的场景:

  • 指定的CSV文件只有一行(应调用一次合并操作,输出写入预期位置)。

  • 指定的CSV文件有两行(应调用两次合并操作,输出写入预期位置)。

  • 输出文件名符合您的期望(无论您的期望是什么)。

一旦进入这个第二阶段,你将开始识别需要使用stub和mock技术的行为。例如,检查文件是否存在——.NET不容易进行模拟,因此您可能需要创建一个适配器接口和类,以便让您的程序与实际的文件系统隔离开来(更不用说实际的CSV文件和邮件合并模板了)。还有其他可用的技术,但这种方法相当标准:

public interface IFileFinder { bool FileExists(string path); }

// Concrete implementation to use in production
public class FileFinder: IFileFinder {
    public bool FileExists(string path) { return File.Exists(path); }
}

public class Merger {
    IFileFinder finder;
    public Merger(IFileFinder finder) { this.finder = finder; }
}
在测试中,你需要传入一个存根实现:
[Test]
[ExpectedException(typeof(FileNotFoundException))]
public void Fails_When_Csv_File_Does_Not_Exist() {

    IFileFinder finder = mockery.NewMock<IFileFinder>();
    Merger      merger = new Merger(finder);
    Stub.On(finder).Method("FileExists").Will(Return.Value(false));

    merger.Merge("csvPath", "templatePath", "outputPath");
}

6

简单的通用指导:

  • 你首先编写单元测试。一开始它们都会失败。
  • 然后你进入被测试的类中,编写代码,直到与每个方法相关的测试都通过。
  • 对于你的类型的每个公共方法都要这样做。

通过编写单元测试,实际上您在以另一种形式指定要求,易于阅读的代码。

从另一个角度看:当您收到一个新的黑盒子类和针对它的单元测试时,您应该阅读单元测试以查看类的功能和行为。

要了解更多关于单元测试的信息,我推荐一本非常好的书:《单元测试艺术》

以下是关于TDD的更多详细信息和示例的StackOverflow文章链接:


2
+1 是指参考《单元测试的艺术》这本书。它是一本很棒的书。 - Steven

2
为了能够进行单元测试,您需要将类与任何依赖项解耦,以便有效地测试类本身。 为此,您需要将任何依赖项注入到该类中。通常情况下,您可以通过在构造函数中传递实现依赖接口的对象来完成此操作。
模拟框架用于创建依赖项的模拟实例,您的类可以在测试期间调用它。您定义模拟以与依赖项相同的方式运行,然后在测试结束时验证其状态。
我建议您使用Rhino mocks并查看文档中的示例,以了解其工作原理。 http://ayende.com/projects/rhino-mocks.aspx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接