How do you perform a unit test with complex I / O?

When you have a simple method like sum (int x, int y), it’s easy to write unit tests. You can verify that the method will correctly sum two integers of the sample, for example, 2 + 3 should return 5, then you will check the same for some "extraordinary" numbers, for example, negative values ​​and zero. Each of them should be a separate unit test, since a single unit test should contain a single assert.

What do you do when you have complex I / O? Take, for example, an XML parser. You can have one method syntax (String xml) that receives a string and returns a Dom object. You can write separate tests that will verify the correctness of certain text node, that the attributes are parsed OK, that the child element of the node belongs to the parent element, etc. For this, I can write simple input, for example

<root><child/></root> 

which will be used to check the relationship between parents and children between nodes, etc. for the rest of the expectations.

Now take a look at follwing 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> 

To check if this method works correctly, I need to check a lot of difficult conditions, for example, this attribute11 and attribute12 belong to element1, that text 1 belongs to child1, etc. I do not want to put a few statements in my unit test. How can i do this?

+4
source share
7 answers

All you need to do is test one aspect of SUT (System Under Test) in a separate test.

 [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; } } 

This approach provides many advantages:

  • Easy defect location - if something is wrong with the attribute parsing, then only attribute tests will fail.
  • Small tests are always easier to understand.

UPD: Look at what Gerard Mesaros (author of xUnit Test Patterns) says on the topic: xunitpatterns

One possible controversial aspect of Testing one condition for a test is what we mean by "one condition." Drivers insist on one test claim for some test. This insistence can be based on the use of the Testcase class for each fixture organizing test methods and naming each test based on the fact that the statement is being verified (for example. AwaitingApprovalFlight.validApproverRequestShouldBeApproved.). Having one statement per test, such a notation is very simple, but it is valid for many other testing methods, if we have for approval in many output fields. Of course, we can often abide by this interpretation by extracting a custom Approval (p. X) or Verification Method (see Custom Assertion), which reduces the number of times the approval method calls one. Sometimes this makes the test more, but when it is not, I will not be too dogmatic insisting on one statement.

+4
source

A few tests.

+1
source

Use some tests. The same restrictions apply. You should check out some normal operational cases, some failed cases, and some edge cases.

Similarly, you can assume that if the sum (x, y) works for some values ​​of x, it will work with other values, you can assume that if the XML parser can parse a sequence of two nodes, it can also parse a sequence of 100 knots.

+1
source

To talk a bit about Ian's short answer: make this XML bit install and run individual individual tests, each with its own statement. This way you are not duplicating the installation logic, but you still have a subtle understanding of what is wrong with your parser.

+1
source

Use Nunit Fluent Syntax

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

I had a similar requirement where I wanted to have 1 Assert for different input sets. please enter the link below that I wrote on the blog.

Writing the best unit tests

This can be applied to your problem. Build a factory class that contains the logic for building β€œcomplex” input sets. The unit test case has only one statement.

Hope this helps.

Thanks, Vijay.

0
source

You can also use your own statements (this is taken from your own question):

attribute11 and attribute12 belong to element1

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

or

('element1 attribute11').length

By the way, this is similar to jQuery. You store this string in a complex graph storage. How could you unit test create a very complex graphics-related database?

0
source

Source: https://habr.com/ru/post/1312716/


All Articles