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

Unit testing 如何对具有复杂输入输出的方法进行单元测试,unit-testing,tdd,Unit Testing,Tdd,当您有一个简单的方法时,比如sum(intx,inty),编写单元测试就很容易了。您可以检查该方法是否将两个样本整数正确相加,例如2+3应返回5,然后您将检查一些“非常”数字是否正确相加,例如负值和零。每个测试都应该是单独的单元测试,因为单个单元测试应该包含单个断言 当你有一个复杂的输入输出时,你会怎么做?以Xml解析器为例。您可以使用一个方法parse(stringxml)来接收字符串并返回Dom对象。您可以编写单独的测试,检查某些文本节点是否正确解析,属性是否正确解析,子节点是否属于父节点等

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

当你有一个复杂的输入输出时,你会怎么做?以Xml解析器为例。您可以使用一个方法parse(stringxml)来接收字符串并返回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>

文本1
文本2

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

多个测试。

使用Nunit Fluent语法

Assert.That( someString,
      Is.Not.Null
      .And.Not.Empty
      .And.EqualTo("foo")
      .And.Not.EqualTo("bar")
      .And.StartsWith("f"));

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


同样,如果sum(x,y)对x的某些值起作用,它也会对其他值起作用,那么您可以假设,如果XML解析器可以解析2个节点的序列,它也可以解析100个节点的序列。

我有一个类似的需求,我希望对各种输入集有1个断言。请点击下面的链接,我在博客上写的

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

希望这有帮助

谢谢,
Vijay.

详细介绍一下Ian简洁的回答:将这部分XML作为设置,并进行单独的测试,每个测试都有自己的断言。这样,您就不会重复设置逻辑,但仍然可以对解析器的错误进行细粒度的洞察。

您可能还需要使用自己的断言(这来自您自己的问题):

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

('attribute11','attribute12')。属于('element1')

('element1 attribute11')。长度


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

您只需要在单独的测试中检查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;
        }
    }
[TestFixture]
公共类XmlParserTest
{
[测试,预期异常(typeof(xmleexception))]
public void FailIfXmlIsNotWellFormed()
{
解析(“”);
}
[测试]
public void ParseShortTag()
{
var doc=Parse(“”);
Assert.That(doc.DocumentElement.Name,Is.EqualTo(“doc”));
}
[测试]
public void ParseFullTag()
{
var doc=Parse(“”);
Assert.That(doc.DocumentElement.Name,Is.EqualTo(“doc”));
}
[测试]
public void ParseInnerText()
{
var doc=解析(“文本1”);
Assert.That(doc.DocumentElement.InnerText,Is.EqualTo(“Text 1”));
}
[测试]
公共无效属性此处为无效属性()
{
var doc=Parse(“”);
Assert.That(doc.DocumentElement.Attributes,Has.Count(0));
}
[测试]
公共void ParseAttribute()
{
var doc=Parse(“”);
Assert.That(doc.DocumentElement.Attributes[0].Name,Is.EqualTo(“attribute11”);
Assert.That(doc.DocumentElement.Attributes[0].Value,Is.EqualTo(“attribute 11 Value”);
}
[测试]
public void ChildNodesInnerTextAtFirstLevel()
{
var doc=Parse(@)
文本1
文本2
");
Assert.That(doc.DocumentElement.ChildNodes,Has.Count(2));
Assert.That(doc.DocumentElement.ChildNodes[0].InnerText,Is.EqualTo(“文本1”);
Assert.That(doc.DocumentElement.ChildNodes[1].InnerText,Is.EqualTo(“Text 2”);
}
//更多测试
.....
私有xml文档解析(字符串xml)
{
var doc=新的XmlDocument();
doc.LoadXml(xml);
退货单;
}
}
这种方法有很多优点:

  • 容易的缺陷定位-如果 属性有问题吗 解析,然后只测试 属性将失败
  • 小测试总是更容易理解
  • UPD:请参阅Gerard Meszaros(xUnit测试模式书的作者)对主题的看法:

    一个可能引起争议的方面 验证每个测试的一个条件是什么 我们所说的“一个条件”。一些测试 驱动程序坚持每个用户一个断言 测试。这种坚持可能是基于 每个装置使用一个Testcase类 试验方法和方法的组织 根据测试的内容命名每个测试 断言正在验证(例如。 等待批准。应批准有效的ProverreRequests。)。 每个测试有一个断言会使 这样的命名很容易,但确实会导致 如果我们有更多的测试方法 在多个输出字段上断言。属于 当然,我们可以经常遵守这一点 通过提取自定义项进行解释 断言(第X页)或验证 方法(请参见自定义断言),该方法 允许我们减少倍数 断言方法调用一个。