用于比较NUnit中文本文件的单元测试
我有一个处理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"); }
测试失败的名称会立即告诉我们什么是损坏的。当然,我们可以使用行测试对相似的测试进行分组。进行单独的测试还可以在许多情况下提供帮助,例如忽略测试,与同事交流测试等等。创建一个片段可以在一秒钟内为我们创建测试,这没什么大不了的,我们将花费更多的时间准备数据。
然后,我们还需要一些测试数据以及基础夹具找到它的方式,请确保为项目设置关于它的规则。如果测试失败,则将实际输出转储到黄金附近的文件中,如果测试通过,则将其清除。这样,我们可以在需要时使用差异工具。如果找不到黄金数据,则测试将失败,并显示相应的消息,但是无论如何都会写入实际输出,因此我们可以检查它是否正确并将其复制为"黄金"。