单元测试XML生成

时间:2009-02-02 16:19:33

标签: c# xml unit-testing nunit

正在正确生成人们推荐用于测试xml的单元测试策略。

我目前的测试看起来很原始,有点像:

[Test]
public void pseudo_test()
{
   XmlDocument myDOC = new XmlDocument();
   mydoc = _task.MyMethodToMakeXMLDoc();

   Assert.AreEqual(myDoc.OuterXML(),"big string of XML")
}

13 个答案:

答案 0 :(得分:21)

首先,正如每个人都说的那样,如果为其定义了架构,则验证XML。 (如果没有,请定义一个。)

但是你可以通过对文档执行XPath查询来构建比这更细粒度的测试,例如:

string xml="Your xml string here" ;
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
path = "/doc/element1[@id='key1']/element2[. = 'value2']";
Assert.IsTrue(doc.SelectSingleNode(path) != null);

这使您不仅可以测试文档是否在语义上有效,还可以测试生成它的方法是否使用您期望的值填充它。

答案 1 :(得分:12)

XMLUnit可能对您有帮助。

答案 2 :(得分:6)

Fluent Assertions是一个出色的库,用于以流畅,易读的方式表达测试断言。它适用于所有主要的单元测试框架。

它还有一些有用的XML功能(全部来自示例here),例如:

xElementA.Should().Be(xElementB);

xDocument.Should().HaveRoot("configuration");
xDocument.Should().HaveElement("settings");

xElement.Should().HaveAttribute("age", "36");
xElement.Should().HaveElement("address");

xAttribute.Should().HaveValue("Amsterdam");

请注意,这适用于LINQ-To-XML而不是原始问题中指定的XmlDocument对象,但是现在我个人发现我使用LINQ-To-XML作为首选。

如果您想要添加更多XML断言以满足您的需求,它也非常容易扩展。

答案 3 :(得分:5)

另一种可能性是使用XmlReader并检查错误计数>这样的事情:

    void CheckXml()
    {
        string _xmlFile = "this.xml";
        string _xsdFile = "schema.xsd"; 
        StringCollection _xmlErrors = new StringCollection();

        XmlReader reader = null;
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationEventHandler += new ValidationEventHandler(this.ValidationEventHandler);
        settings.ValidationType = ValidationType.Schema;
        settings.IgnoreComments = chkIgnoreComments.Checked;
        settings.IgnoreProcessingInstructions = chkIgnoreProcessingInstructions.Checked;
        settings.IgnoreWhitespace = chkIgnoreWhiteSpace.Checked;
        settings.Schemas.Add(null, XmlReader.Create(_xsdFile));
        reader = XmlReader.Create(_xmlFile, settings);
        while (reader.Read())
        {
        }
        reader.Close();
        Assert.AreEqual(_xmlErrors.Count,0);
    }    

    void ValidationEventHandler(object sender, ValidationEventArgs args)
    {
        _xmlErrors.Add("<" + args.Severity + "> " + args.Message);
    }

答案 4 :(得分:4)

验证XML架构或DTD,还检查节点是否具有您期望的值的键。

答案 5 :(得分:3)

如果您有一个预期输出的标准格式,为什么不创建XML模式或DTD并对其进行验证。这不依赖于数据,因此将是灵活的。在设计系统时,定义XML的形成方式也很有用。

答案 6 :(得分:1)

marianor的

This blog post给出了一种比较XElement结构的轻量级方法,因此我将在处理XMLUnit之前尝试一下。

  

首先要做的是规范化两个XML ...使用Linq ...在两个元素都被规范化之后,只需比较两个字符串。

通过对元素和属性名称进行排序来规范化XML。

答案 7 :(得分:0)

为什么不假设某些商业xml解析器是正确的并且针对它验证你的xml代码?类似的东西。

Assert.IsTrue(myDoc.Xml.ParseOK)

除此之外,如果你想要彻底,我会说你必须自己构建一个解析器并验证xml规范所需的每个规则。

答案 8 :(得分:0)

使用XmlSchema类对XSD架构进行验证。我认为它在System.XML下找到了。 另一种选择是编写序列化类(XMLSerializer)以将XML反序列化为对象。获得的结果是它将隐式验证您的结构,之后可以使用生成的对象轻松访问这些值以进行测试。

答案 9 :(得分:0)

使用Schema进行验证的另一个原因是,虽然XML节点是显式排序的,但XML属性不是。

所以你的字符串比较:

Assert.AreEqual(myDoc.OuterXML(),"big string of XML")
如果属性的顺序不同,

将失败,如果手动创建一个XML并且以编程方式创建另一个XML,则很容易发生。

答案 10 :(得分:0)

验证生成的文档是否格式正确 验证生成的文档是否有效 验证生成的文档是否正确。

据推测,您正在使用有用数据制作XML文档,因此您需要确保对测试具有正确的输入覆盖率。我看到的最常见的问题是

  • 错误转义元素
  • 错误转义属性
  • 错误地转义了元素名称
  • 错误地转义属性名称

因此,如果您还没有这样做,则需要查看XML规范,以了解每个地方允许的内容。

每次测试应该进行多少次“检查”并不是立即清楚的。我想,这将在很大程度上取决于你的问题空间中的单位。每个单元测试检查XML中是否正确表达了一条数据似乎是合理的。在这种情况下,我同意罗伯特的观点,即在一个XPath位置找到正确数据的简单检查是最好的。

对于要检查整个文档的大型自动化测试,我发现有效的是获得预期结果,这也是一个文档,并逐个节点地遍历它,使用XPath表达式查找实际文档中的相应节点,然后应用两个节点中编码的数据的正确比较。

通过这种方法,您通常希望立即捕获所有故障,而不是在第一次故障时中止,因此您可能需要了解如何跟踪发生不匹配的位置。

通过更多工作,您可以将某些元素类型识别为从测试中获得(如时间戳),或者验证它们是指向等效节点的指针,或者......您想要的任何类型的自定义验证

答案 11 :(得分:0)

我计划使用这个新的Approval Testing库来帮助进行XML测试。

它似乎非常适合这项工作,但我自己先阅读,因为我没有使用它的经验。

答案 12 :(得分:0)

您可以使用DTD检查生成的xml的有效性。

要测试正确的内容,我会选择XMLUnit

使用XMLUnit断言xml:

XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true);

Diff diff = new Diff(expectedDocument, obtainedDocument);
XMLAssert.assertXMLIdentical("xml invalid", diff, true);

您可能遇到的一件事是生成的xml可能包含更改的标识符(id / uid属性或类似)。这可以通过在断言生成的xml时使用DifferenceListener来解决。

此类DifferenceListener的示例实现:

public class IgnoreVariableAttributesDifferenceListener implements DifferenceListener {

    private final List<String> IGNORE_ATTRS;
    private final boolean ignoreAttributeOrder;

    public IgnoreVariableAttributesDifferenceListener(List<String> attributesToIgnore, boolean ignoreAttributeOrder) {
        this.IGNORE_ATTRS = attributesToIgnore;
        this.ignoreAttributeOrder = ignoreAttributeOrder;
    }

    @Override
    public int differenceFound(Difference difference) {
        // for attribute value differences, check for ignored attributes
        if (difference.getId() == DifferenceConstants.ATTR_VALUE_ID) {
            if (IGNORE_ATTRS.contains(difference.getControlNodeDetail().getNode().getNodeName())) {
                return RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
            }
        }
        // attribute order mismatch (optionally ignored)
        else if (difference.getId() == DifferenceConstants.ATTR_SEQUENCE_ID && ignoreAttributeOrder) {
            return RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
        }
        // attribute missing / not expected
        else if (difference.getId() == DifferenceConstants.ATTR_NAME_NOT_FOUND_ID) {
            if (IGNORE_ATTRS.contains(difference.getTestNodeDetail().getValue())) {
                return RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
            }
        }

        return RETURN_ACCEPT_DIFFERENCE;
    }

    @Override
    public void skippedComparison(Node control, Node test) {
        // nothing to do
    }
}

使用DifferenceListener:

    XMLUnit.setIgnoreWhitespace(true);
    XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true);

    Diff diff = new Diff(expectedDocument, obtainedDocument);
    diff.overrideDifferenceListener(new IgnoreVariableAttributesDifferenceListener(Arrays.asList("id", "uid"), true));

    XMLAssert.assertXMLIdentical("xml invalid", diff, true);