【问题标题】:Learning TDD with a simple example用一个简单的例子学习 TDD
【发布时间】:2011-03-25 20:43:41
【问题描述】:

我正在尝试学习 TDD,但很难弄清楚我需要编写一个小应用程序来测试什么/如何测试。

应用程序的(稍微简化的)规范如下:

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

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

为了清楚起见,我并不是在问我将如何编写这样的应用程序,因为我有信心如果我只是继续并开始的话,我知道该怎么做。但是,如果我想使用 TDD 来完成它,将不胜感激有关编写测试的一些指导,因为我猜我不想测试读取真正的 csv 文件,或者测试执行合并的第 3 方组件或转换为pdf。

我认为只是一些通用的 TDD 指导会很有帮助!

【问题讨论】:

    标签: c# tdd


    【解决方案1】:

    我会首先考虑程序每个步骤的场景,从失败案例及其预期行为开始:

    • 用户提供了一个空的 csv 文件位置(抛出一个ArgumentNullException)。

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

    • 用户指定的 csv 文件不存在(随便你认为合适的)。

    接下来,为每个场景编写一个测试并确保它失败。接下来,编写足够的代码以使测试通过。对于其中一些情况,这很容易,因为使您的测试通过的代码通常是最终代码:

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

    之后,进入标准场景:

    • 指定的csv文件只有一行(merge应该被调用一次,输出写入到预期的位置)。

    • 指定的 csv 文件有两行(merge 应该被调用两次,输出写入到预期的位置)。

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

    等等。一旦进入第二阶段,您将开始识别要存根和模拟的行为。例如,检查文件是否存在 - .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");
    }
    

    【讨论】:

      【解决方案2】:

      简单的一般指导:

      • 您首先编写单元测试。一开始 他们都失败了。
      • 然后你进入被测类 并编写代码,直到相关的测试 每个方法都通过。
      • 对每个公共方法执行此操作 你的类型。

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

      从另一个角度来看:当您收到一个新的黑盒类并对其进行单元测试时,您应该阅读单元测试以了解该类的作用和行为方式。

      要阅读有关单元测试的更多信息,我推荐一本非常好的书:Art Of Unit Testing

      以下是 StackOverflow 上有关 TDD 的文章的几个链接,以获取更多详细信息和示例:

      【讨论】:

      • +1 用于引用单元测试的艺术。这是一本好书。
      【解决方案3】:

      为了能够进行单元测试,您需要将类与任何依赖项分离,这样您就可以有效地测试类本身。

      为此,您需要将任何依赖项注入到类中。您通常会通过将实现依赖接口的对象传递到构造函数中的类来执行此操作。

      模拟框架用于创建您的依赖项的模拟实例,您的类可以在测试期间调用该实例。您将模拟定义为与您的依赖项相同的行为方式,然后在测试结束时验证它的状态。

      我建议您玩一下 Rhino 模拟并查看文档中的示例以了解其工作原理。

      http://ayende.com/projects/rhino-mocks.aspx

      【讨论】:

        猜你喜欢
        • 2011-05-10
        • 1970-01-01
        • 2017-03-27
        • 1970-01-01
        • 2018-11-24
        • 1970-01-01
        • 1970-01-01
        • 2012-11-30
        • 1970-01-01
        相关资源
        最近更新 更多