代码之家  ›  专栏  ›  技术社区  ›  Dan

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

  •  4
  • Dan  · 技术社区  · 14 年前

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

    当你有一个复杂的输入输出时,你会怎么做?以XML解析器为例。您可以让一个方法parse(string xml)接收字符串并返回一个dom对象。您可以编写单独的测试来检查特定的文本节点是否被正确解析、属性是否被正确解析、子节点是否属于父节点等。对于所有这些,我可以编写一个简单的输入,例如

    <root><child/></root>
    

    这将用于检查节点之间的父子关系等其他期望。

    现在,看看下面的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>
    

    为了检查方法是否正确工作,我需要检查许多复杂的条件,比如attribute11和attribute12属于element1,文本1属于child1等等。 我不想在单元测试中放置多个断言。 我怎样才能做到?

    7 回复  |  直到 14 年前
        1
  •  4
  •   Yauheni Sivukha    14 年前

    您所需要的就是在单独的测试中检查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;
            }
        }
    

    这种方法有很多优点:

    1. 容易定位缺陷-如果 属性有问题 解析,然后仅测试 属性将失败。
    2. 小测试总是容易理解的。

    upd:看看Gerard Meszaros(Xunit测试模式书的作者)对主题的看法: xunitpatterns

    一个可能有争议的方面 验证每个测试的一个条件是什么 我们的意思是“一个条件”。一些测试 驱动程序坚持每个断言一个 测试。这种坚持可能是基于 每个fixture使用一个testcase类 试验方法的组织和 根据每个测试的名称 断言正在验证(例如 正在等待批准的指示灯。应验证validaproverRequest)。 每个测试有一个断言 这样的命名很容易,但它确实会导致 如果我们有更多的测试方法 在许多输出字段上断言。的 当然,我们可以经常遵守 通过提取自定义进行解释 断言(第X页)或验证 方法(参见自定义断言) 允许我们减少倍数 断言方法调用为一个。 有时这会使测试更加 可读,但如果不可读,我 不会太武断的 坚持一个主张。

        2
  •  1
  •   Ian P    14 年前

    多重测试。

        3
  •  1
  •   Jack Ryan    14 年前

    使用多个测试。同样的限制也适用。您应该测试一些正常的操作案例、一些失败案例和一些边缘案例。

    正如您可以假定的那样,如果sum(x,y)适用于x的某些值,它将适用于其他值,那么您可以假定,如果XML解析器可以解析2个节点的序列,那么它也可以解析100个节点的序列。

        4
  •  1
  •   Carl Manaster    14 年前

    详细说明一下Ian的简短回答:将这一点的XML作为设置,并有单独的测试,每个测试都有自己的断言。这样,您就不会复制设置逻辑,但是您仍然可以对解析器的问题有很好的了解。

        5
  •  0
  •   P.K    14 年前

    使用nunit-fluent语法

    Assert.That( someString,
          Is.Not.Null
          .And.Not.Empty
          .And.EqualTo("foo")
          .And.Not.EqualTo("bar")
          .And.StartsWith("f"));
    
        6
  •  0
  •   vijaysylvester    14 年前

    我有一个类似的需求,我想对各种输入集有一个断言。请检查下面的链接,这是我在博客上写的。

    Writing better unit tests

    这也适用于你的问题。构造工厂类,该类包含用于构造“复杂”输入集的逻辑。单元测试用例只有一个断言。

    希望这有帮助。

    谢谢, Vijay。

        7
  •  0
  •   Gutzofter    14 年前

    您可能还需要使用自己的断言(这是从您自己的问题中得出的):

    属性11和属性12属于元素1

    ('attribute11 ', 'attribute12').belongsTo('element1');

    ('element1 attribute11').length

    这个

    顺便说一句,这类似于jquery。您将这个字符串存储在一个复杂的图形存储库中。如何对一个非常复杂的图形连接数据库进行单元测试?