5

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

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

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

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

有没有我应该用于这种测试的模式,还是人们倾向于编写数以万计的 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...
}

更新

我对测试差异功能不感兴趣。我只是想用它来产生更具可读性的失败。

4

5 回答 5

5

至于具有不同数据的多个测试,请使用 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);
 }
于 2008-09-26T22:16:05.963 回答
2

您可能要求对“黄金”数据进行测试。我不知道这种测试是否有特定的术语可以在全球范围内接受,但我们就是这样做的。

创建基夹具类。它基本上有“void DoTest(string fileName)”,它将特定文件读入内存,执行抽象转换方法“string Transform(string text)”,然后从同一个地方读取fileName.gold,并将转换后的文本与预期的文本进行比较. 如果内容不同,则抛出异常。抛出的异常包含第一个差异的行号以及预期和实际行的文本。由于文本是稳定的,这通常足以立即发现问题。请务必用“预期:”和“实际:”标记行,否则在查看测试结果时您将永远猜测哪个是哪个。

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

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

失败测试的名称会立即告诉你什么是坏的。当然,您可以使用行测试对类似的测试进行分组。进行单独的测试在许多情况下也有帮助,例如忽略测试、将测试传达给同事等等。创建一个可以在一秒钟内为您创建测试的代码段并不是什么大问题,您将花费更多时间准备数据。

然后你还需要一些测试数据和你的基本夹具找到它的方法,一定要为项目设置关于它的规则。如果测试失败,将实际输出转储到黄金附近的文件中,如果测试通过则将其删除。这样您就可以在需要时使用差异工具。当没有找到黄金数据时,测试失败并显示适当的消息,但无论如何都会写入实际输出,因此您可以检查它是否正确并将其复制为“黄金”。

于 2008-09-28T06:28:00.890 回答
0

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

第 32 行第 12 列 - 在预期为 'y' 时找到'x'

您可以选择通过显示多行输出来增强它

第 32 行第 12 列的差异,显示第一个差异
A = 这是在x st
B = 这是在e sts

请注意,作为一项规则,我通常只会通过我的代码生成您拥有的两个流之一。我会从测试/文本文件中获取另一个,通过眼睛或其他方法验证包含的数据是正确的!

于 2008-09-26T21:43:36.117 回答
0

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

然后,您只需添加新的文本文件即可编写新的测试。

于 2008-09-26T21:44:29.003 回答
0

我可能会使用 XmlReader 遍历文件并比较它们。当我遇到差异时,我会在文件不同的位置显示一个 XPath。

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

于 2008-09-27T05:19:08.957 回答