用于比较NUnit中文本文件的单元测试

时间:2020-03-06 14:48:38  来源:igfitidea点击:

我有一个处理2个xml文件并生成文本文件的类。

我想写一堆单元/集成测试,它们可以为此类单独通过或者失败,该测试执行以下操作:

  • 对于输入A和B,生成输出。
  • 将生成的文件的内容与预期的内容输出进行比较
  • 当实际内容与预期内容不同时,请失败并显示一些有关差异的有用信息。

下面是该类的原型以及我在单元测试中的第一个步骤。

我应该在这种测试中使用某种模式吗?还是人们倾向于编写成千上万的TestX()函数?

有没有更好的办法来解决NUnit的文本文件差异?我应该嵌入文本文件差异算法吗?

class ReportGenerator
{
    string Generate(string inputPathA, string inputPathB)
    {
        //do stuff
    }
}
[TextFixture]
public class ReportGeneratorTests
{
     static Diff(string pathToExpectedResult, string pathToActualResult)
     {
         using (StreamReader rs1 = File.OpenText(pathToExpectedResult))
         {
             using (StreamReader rs2 = File.OpenText(pathToActualResult))
             {
                 string actualContents = rs2.ReadToEnd();
                 string expectedContents = rs1.ReadToEnd();                  

                 //this works, but the output could be a LOT more useful.
                 Assert.AreEqual(expectedContents, actualContents);
             }
         }
     }

     static TestGenerate(string pathToInputA, string pathToInputB, string pathToExpectedResult)
     {
          ReportGenerator obj = new ReportGenerator();
          string pathToResult = obj.Generate(pathToInputA, pathToInputB);
          Diff(pathToExpectedResult, pathToResult);
     }

     [Test]
     public void TestX()
     {
          TestGenerate("x1.xml", "x2.xml", "x-expected.txt");
     }

     [Test]
     public void TestY()
     {
          TestGenerate("y1.xml", "y2.xml", "y-expected.txt");
     }

     //etc...
}

更新

我对测试diff功能不感兴趣。我只想用它来产生更多可读的错误。

解决方案

不必调用.AreEqual,我们可以自己解析两个输入流,保留行和列的计数并比较内容。一旦发现差异,我们就可以生成一条消息,例如...

Line 32 Column 12 - Found 'x' when 'y' was expected

我们可以选择显示多行输出来增强它

Difference at Line 32 Column 12, first difference shown

  A = this is a txst

  B = this is a tests

注意,通常,我通常只通过代码生成我们拥有的两个流之一。我从测试/文本文件中抓取了另一个文件,用肉眼或者其他方法验证了其中包含的数据是正确的!

我可能会写一个包含循环的单个单元测试。在循环内部,我将读取2个xml文件和一个diff文件,然后对xml文件进行比较(不将其写入磁盘),并将其与从磁盘读取的diff文件进行比较。文件将被编号,例如a1.xml,b1.xml,diff1.txt; a2.xml,b2.xml,diff2.txt; a3.xml,b3.xml,diff3.txt等,当找不到下一个数字时,循环将停止。

然后,我们只需添加新的文本文件就可以编写新的测试。

对于具有不同数据的多个测试,请使用NUnit RowTest扩展:

using NUnit.Framework.Extensions;

[RowTest]
[Row("x1.xml", "x2.xml", "x-expected.xml")]
[Row("y1.xml", "y2.xml", "y-expected.xml")]
public void TestGenerate(string pathToInputA, string pathToInputB, string pathToExpectedResult)
 {
      ReportGenerator obj = new ReportGenerator();
      string pathToResult = obj.Generate(pathToInputA, pathToInputB);
      Diff(pathToExpectedResult, pathToResult);
 }

我可能会使用XmlReader来遍历文件并进行比较。当我遇到差异时,我将显示一个XPath,指向文件不同的位置。

PS:但是实际上,对我来说,只需将整个文件简单读取为一个字符串并比较两个字符串就足够了。对于报告,足以看到测试失败。然后,当我进行调试时,我通常使用Araxis Merge来比较文件,以查看确切的问题所在。

我们可能正在要求针对"黄金"数据进行测试。我不知道这种测试是否在世界范围内被接受,但这就是我们的工作方式。

创建基本灯具类。它基本上具有" void DoTest(字符串文件名)",它将特定文件读入内存,执行抽象转换方法" string Transform(字符串文本)",然后从同一位置读取fileName.gold并将转换后的文本与预期内容进行比较。如果内容不同,则会引发异常。引发的异常包含第一个差异的行号以及预期行和实际行的文本。由于文本是稳定的,因此通常这是足够的信息,可以立即发现问题。确保用" Expected:"和" Actual:"标记行,否则,在查看测试结果时,我们将永远猜测是哪个。

然后,我们将具有特定的测试装置,在其中实现正确工作的Transform方法,然后进行如下所示的测试:

[Test] public void TestX() { DoTest("X"); }
[Test] public void TestY() { DoTest("Y"); }

测试失败的名称会立即告诉我们什么是损坏的。当然,我们可以使用行测试对相似的测试进行分组。进行单独的测试还可以在许多情况下提供帮助,例如忽略测试,与同事交流测试等等。创建一个片段可以在一秒钟内为我们创建测试,这没什么大不了的,我们将花费更多的时间准备数据。

然后,我们还需要一些测试数据以及基础夹具找到它的方式,请确保为项目设置关于它的规则。如果测试失败,则将实际输出转储到黄金附近的文件中,如果测试通过,则将其清除。这样,我们可以在需要时使用差异工具。如果找不到黄金数据,则测试将失败,并显示相应的消息,但是无论如何都会写入实际输出,因此我们可以检查它是否正确并将其复制为"黄金"。