2011-10-14 37 views
7

这是我的c#单元测试设计问题:C#单元测试设计问题:如何减少单元测试写入冗余?

我有一个测试,我想运行一些修改文件的代码。我的测试将运行修改文件的代码,然后检查结果。目前相当直截了当......

问题是,我有大约10个文件(很快会更多),我想运行相同的单元测试。我不想为每个文件写一个新的单元测试,当测试本身真的是一样的时候。

我可以编写一个测试来查询文件夹中的文件,然后在每个文件上运行测试逻辑,但是使用此方法只会报告整个文件集的一个通过/失败结果。

编号喜欢创建某种动态系统,其中我放置在特定文件夹中的每个文件都通过相同的单元测试运行。因此,如果文件夹中有25个文件,则测试运行25次,单元测试reults报告运行25个测试,并且包含每个测试的单独通过/失败。

任何想法如何或如果这可以在C#单元测试中完成?或者与一个nunit测试?

谢谢!我希望这不是一个重复的问题。我环顾四周,但找不到任何东西。

+1

有些人会争辩说,涉及IO(在这种情况下操纵文件系统)的测试不是单元测试。另一种方法是将IO操作表示为可注入的依赖关系。然而,大多数的System.IO类型(当然还有静态)并不适用于此。我使用的一种方法是创建简单方法转发类型和相应接口的代理程序集。这允许您保留这个瘦代理层未经测试,并且您的逻辑层完全经过_unit_测试。微软莫尔斯有效地做到了这一点,但这仍然是一个在制品。 –

回答

3

另一种选择是使用T4模板动态生成基于文件的目录中的编号为你测试。将这个“.tt”文件添加到你的单元测试项目中。

现在,无论何时只要在Visual Studio中按下“在解决方案中运行所有测试”按钮之前进行构建,就应该在单元测试中为文件名中的所有文件生成单元测试。然后,您的测试运行应该包含所有测试文件及其状态的好列表。

<#@ template debug="false" hostspecific="true" language="C#v3.5" #> 
<#@ assembly name="System.Core.dll" #> 
<#@ assembly name="System.Data.dll" #> 
<#@ import namespace="System.IO" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ import namespace="System.Linq" #> 
<#@ output extension=".cs" #> 
<# 
      string inputDirectory = @"d:\temp"; 
      var files = System.IO.Directory.GetFiles(inputDirectory); 
#> 
using System; 
using System.Text; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace TestProject1 
{ 
    [TestClass] 
    public class UnitTest1 
    { 
     <# foreach (string filePath in files) { #> 
     [TestMethod] 
     public void TestingFile_<#=System.IO.Path.GetFileNameWithoutExtension(filePath).Replace(' ','_').Replace('-','_')#>() 
     { 
     File currentFile = System.IO.File.Open(@"<#= filePath #>", FileMode.Open); 
     // TODO: Put your standard test code here which will use the file you opened above 
     } 
     <# } #> 
    } 
} 
+1

这是很棒的东西!直到现在我还不知道这个功能,所以我真的很高兴我终于问了一个关于stackoverflow的问题! :) – fj40bryan

+1

谢谢大家为我的问题提供很好的输入! – fj40bryan

10

这听起来像你需要的是一个参数化测试用例:见http://www.nunit.org/index.php?p=testCaseSource&r=2.5

所以:

[TestFixture] 
public class Tests 
{ 
    static string[] FileNames = new string[] 
        { "mary.txt", "mungo.txt", "midge.txt" }; 

    [Test, TestCaseSource("FileNames")] 
    public void TestMethod(string fileName) 
    { 
     Assert.That(File.Exists(fileName)); 
    } 
} 

[TestCaseSource]属性告诉NUnit的从字符串数组获取字符串参数的值。

但是,该方法需要静态文件名的常量。如果你宁愿有一个编程方法,从数据库中读取文件或类似尝试一个工厂类,像这样:

[TestFixture] 
public class Tests 
{ 
    [Test, TestCaseSource(typeof(FilenameFactory), "FileNames")] 
    public bool FileCheck(string fileName) 
    { 
     return File.Exists(fileName); 
    } 
} 

public class FilenameFactory 
{ 
    public static IEnumerable FileNames 
    { 
     get 
     { 
      foreach (var filename in 
        Directory.EnumerateFiles(Environment.CurrentDirectory)) 
      { 
       yield return new TestCaseData(filename).Returns(true); 
      } 
     } 
    } 
} 

TestCaseData类产生“测试案例”,它具有可以通过流畅的设定预期接口。

+0

这也是一个非常棒的解决方案。谢谢你,杰里米。 – fj40bryan

0

首先,测试依赖外部来源,如磁盘上的文件不是严格的单元测试(意见)。

您正在寻找的是一种数据驱动的测试方法,然后您应该寻找支持它的工具,如MSTestMBunit。 (按照链接了解更多信息)。

然后,你可以做这样的事情(用MbUnit的):

[TestFixture] 
public class MyTestFixture 
{ 
    [Test] 
    public void WordCounter([TextData(ResourcePath = "Data.txt")] string text) 
    { 
     var wordCounter = new WordCounter(text); 
     int count = wordCounter.Count("Firecrest"); 
     Assert.AreEqual(4, count); // Should not include the plural form "Firecrests". 
    } 
}