1

我正在编写一个简单的文件转换器,它将获取一个 XML 文件并将其转换为 CSV,反之亦然。

我已经实现了 2 个类,XMLtoCSV 和 CSVtoXML,它们都实现了一个 Convert 方法,该方法采用输入文件路径和过滤器文本,并通过给定的过滤器过滤 XML 并执行转换。(例如,如果 XML 包含员工详细信息,我们可能希望对其进行过滤,以便仅检索来自某个部门的员工并将其转换为 CSV 文件)。

我有一个单元测试来测试这个 Convert 方法。在其中我指定输入文件路径和过滤器字符串并调用转换函数并断言布尔结果,但我还需要测试过滤是否有效并且转换是否已完成。

我的问题是你真的需要访问文件 IO 并通过单元测试进行过滤和转换吗?这不是集成测试吗?如果不是,那么我如何在不实际转换文件并返回结果的情况下断言过滤已经工作?我考虑过 Moq'ing Convert 方法,但这并不一定证明我的 Convert 方法工作正常。

任何帮助/建议表示赞赏。

谢谢

4

2 回答 2

4

我会建议你在你的类中使用流,并在应用程序中传递文件流,并在单元测试中传递“假”或 StringStream。如果您决定从 WebService 或任何其他方式获取此 xml,这将使您更加灵活 - 您只需要传递一个流,而不是文件路径。

于 2013-09-18T18:32:27.870 回答
2

我的问题是你真的需要访问文件 IO 并通过单元测试进行过滤和转换吗?这不是集成测试吗?

准确地说 - 在这种情况下,您正在测试 3 件事 - 文件 IO 系统、实际文件内容Convert方法本身。

我认为您需要考虑重组代码以使其更适合单元测试(这不是对您的代码的批评!)。考虑您对Convert方法的定义:

在其中我指定输入文件路径和过滤器字符串

所以你的Convert方法实际上是在做件事——打开/读取文件,以及转换内容。您需要改变一些事情,以便 Convert 方法只做一件事 - 具体来说,执行字符串(或实际上是流)的转换,而无需提及它的来源。

这样,您可以Convert通过为方法提供您在单元测试中定义的字符串来正确测试该方法- 一个测试使用已知的好数据,一个测试使用已知的坏数据。

例如

void Convert_WithGoodInput_ReturnsTrue()
{
   var input="this is a piece of data I know is good and should pass";
   var sut = new Converter(); //or whatever it's called :)
   bool actual = sut.Convert(input);
   Assert.AreEqual(true,actual,"Convert failed to convert good data...");
}

void Convert_WithBadInput_ReturnsFalse()
{
   var input="this is a piece of data I know is BAD and should Fail. Bad Data! Bad!";
   var sut = new Converter(); //or whatever it's called :)
   bool actual = sut.Convert(input);
   Assert.AreEqual(false,actual,"Convert failed to complain about bad data...");
}

当然,在您的Convert方法中,您正在做各种神秘而奇妙的事情,此时您可能会查看该方法,看看是否可以将其拆分为多个内部方法,其功能可能由单独的类提供,您将其作为Converter类的依赖项提供,而这些又可以单独进行测试。

通过这样做,您将能够测试转换器方法的功能,并且您将能够开始使用 Mocks,以便您也可以测试它的功能行为- 例如确保frobber仅调用一次, 并且总是在gibber,之前gibber总是调用munger, 等等。

奖金

但是等等,还有更多!!!!1!- 一旦你的转换器类/方法被安排成这样,你会突然发现你现在可以实现一个 XML 到制表符分隔,或 XML 到 JSON,或 XML 到???? 只需编写相关组件并将其插入 Converter 类即可。松耦合FTW!

例如(在这里我只是在想象你的转换函数的胆量可能如何工作)

public class Converter
{
   public Converter(ISourceReader reader, IValidator validator, IFilter filter,IOutputformatter formatter)
   {
      //boring saving of dependencies to local privates here...
   }

   public bool Convert(string data,string filter)
   {
       if (!validator.Validate(data)) return false;
       var filtered = filter.Filter(data); 
       var raw = reader.Tokenise(filtered);
       var result = formatter.Format(raw);
       //and so on
       return true; //or whatever...
   }    
}

当然,我并不是要告诉你如何编写代码,但上面是一个非常可测试的单元和功能测试类,因为你可以在你喜欢的地方混合和匹配 Mocks、Stubs 和 Reals。

于 2013-09-18T23:34:36.720 回答