如何对具有复杂输入输出的方法进行单元测试

Dan*_*Dan 5 tdd unit-testing

当您有一个简单的方法(例如sum(int x,int y))时,编写单元测试很容易。您可以检查该方法是否正确求和两个样本整数,例如2 + 3应该返回5,然后您将检查某些“非常规”数字是否相同,例如负值和零。这些每个都应该是单独的单元测试,因为单个单元测试应该包含单个断言。

当您有复杂的输入输出时,该怎么办?以Xml解析器为例。您可以使用单个方法parse(String xml)来接收String并返回一个Dom对象。您可以编写单独的测试,以检查某些文本节点是否正确解析,属性是否正确解析,该子节点属于父级等。对于所有这些,我都可以编写一个简单的输入,例如

<root><child/></root>
Run Code Online (Sandbox Code Playgroud)

它将用于检查节点之间的父子关系,以此类推。

现在,看看以下Xml:

<root>
  <child1 attribute11="attribute 11 value" attribute12="attribute 12 value">Text 1</child1>
  <child2 attribute21="attribute 21 value" attribute22="attribute 22 value">Text 2</child2>
</root>
Run Code Online (Sandbox Code Playgroud)

为了检查该方法是否正常工作,我需要检查许多复杂的条件,例如attribute11和attribute12属于element1,Text 1属于child1等。我不想在单元测试中放置多个断言。我该怎么做?

Yau*_*kha 5

您所需要的-是在单独的测试中检查SUT(被测系统)的一个方面。

[TestFixture]
    public class XmlParserTest
    {
        [Test, ExpectedException(typeof(XmlException))]
        public void FailIfXmlIsNotWellFormed()
        {
            Parse("<doc>");
        }

        [Test]
        public void ParseShortTag()
        {
            var doc = Parse("<doc/>");

            Assert.That(doc.DocumentElement.Name, Is.EqualTo("doc"));
        }

        [Test]
        public void ParseFullTag()
        {
            var doc = Parse("<doc></doc>");

            Assert.That(doc.DocumentElement.Name, Is.EqualTo("doc"));
        }

        [Test]
        public void ParseInnerText()
        {
            var doc = Parse("<doc>Text 1</doc>");

            Assert.That(doc.DocumentElement.InnerText, Is.EqualTo("Text 1"));
        }

        [Test]
        public void AttributesAreEmptyifThereAreNoAttributes()
        {
            var doc = Parse("<doc></doc>");

            Assert.That(doc.DocumentElement.Attributes, Has.Count(0));
        }

        [Test]
        public void ParseAttribute()
        {
            var doc = Parse("<doc attribute11='attribute 11 value'></doc>");

            Assert.That(doc.DocumentElement.Attributes[0].Name, Is.EqualTo("attribute11"));
            Assert.That(doc.DocumentElement.Attributes[0].Value, Is.EqualTo("attribute 11 value"));
        }

        [Test]
        public void ChildNodesInnerTextAtFirstLevel()
        {
            var doc = Parse(@"<root>
              <child1>Text 1</child1>
              <child2>Text 2</child2>
            </root>");

            Assert.That(doc.DocumentElement.ChildNodes, Has.Count(2));
            Assert.That(doc.DocumentElement.ChildNodes[0].InnerText, Is.EqualTo("Text 1"));
            Assert.That(doc.DocumentElement.ChildNodes[1].InnerText, Is.EqualTo("Text 2"));
        }

        // More tests 
        .....

        private XmlDocument Parse(string xml)
        {
            var doc = new XmlDocument();

            doc.LoadXml(xml);

            return doc;
        }
    }
Run Code Online (Sandbox Code Playgroud)

这种方法具有很多优点:

  1. 易于定位缺陷-如果属性解析出现问题,则仅对属性的测试将失败。
  2. 小型测试总是更容易理解

UPD:参见Gerard Meszaros(xUnit测试模式书的作者)对以下主题的看法xunitpatterns

每个测试验证一个条件的一个可能有争议的方面是我们所说的“一个条件”。一些测试驱动程序坚持每个测试一个声明。这种坚持可以基于使用“测试方法”的“每个灯具”组织的“测试用例类”,并基于一个断言正在验证的内容(例如AwaitingApprovalFlight.validApproverRequestShouldBeApproved)来命名每个测试。每个测试只有一个断言,这样的命名非常容易,但是如果我们必须对许多输出字段进行断言,确实会导致更多的测试方法。当然,我们通常可以通过提取自定义断言(第X页)或验证方法(请参阅自定义断言)来遵循这种解释,这使我们可以将多个断言方法调用减少为一个。