Testing XPathNavigator

By | June 3, 2013

In my previous post about XPathNavigator, I explained in what circumstances the default implementation of XPathNavigator is troublesome. I went over the design of the class and highlighted how that design helps us re-implement XPathNavigator to address the issue.

Testing XPathNavigator

First things first, before attacking the new implementation proper, we want to make sure our implementation is compatible with the default implementation. To do so, we will write tests that will be run both against the Microsoft implementation as well as our implementation once it exists. Our goal here is really twofold. On the one hand, we want to ensure the existing implementation actually works as documented. On the other hand, we want to check our own implementation against the specification tests.

What should we test ?

XPathNavigator is a complex class. So we want to limit my tests to what actually matters for the new implementation. Otherwise, we may be writing literally hundreds of tests.

It is obviously not necessary to test methods that will not be re-implemented. In the previous post, we identified a subset of methods that we will need to re-implement. All other methods are somehow using this basic subset to implement their functionality. The subset is the list of abstract members:

As you can see, we have two distinct groups:

  • The abstract properties expose information about the current node. Our tests will ensure that we get consistent information for all types of node.
  • The abstract methods are all concerned about moving the navigator to another node. The tests need to check that the move operations result in the navigator pointing to the right node given a known starting position.

How should we test it ?

We will test the properties by setting up a XPathNavigator that points to specific nodes of an xml document. Once setup, we simply check the properties expose consistent values. We will test the Move() operations in a very similar way. We will setup the XPathNavigator instance on a specific node, execute the Move() operation we want to test and then check that the XPathNavigator yields values through its properties that are consistent with the navigator’s new position.

This is actually very similar. The only difference is the Move() operation. The similarity will let us factor our most of the test code into a few utility functions.

CanMoveImpl() acts as a parametrized test. It takes 2 arguments:

  • args: a MoveTestArgs instance. This argument describes the test’s original state and the resulting state we should test against.
  • moveOperation: A delegate to the Move() operation to test. Passing the operation to test as a parameter let us also write non-Move() tests by simply passing a no-op callback.

NUnit: I am using NUnit to write the unit tests. It is only a matter of preference. You can adapt the tests to work against another testing framework such as Microsoft Unit Testing Framework. I find NUnit to be simple to use, non-obstrusive and very flexible. 

CanMoveImpl() is called by actual test methods like the following:

It is a parametrized test. The TestCaseSource attribute tells NUnit which method to call to get the MoveTestArgs instance for each test.

Method CanmoveToNext_Source() returns each test case for a given operation. In the above example, we have the test cases for “when position on document root, MoveToNext() should fail”, “When positioned on element whith no next sibling, MoveToNext() should fail” and “when positioned on an element with a next sibling, MoveToNext() should succeed and point to the specific node”.

Each test case is defined by specifying values for the fields of class CanMoveArgs.

Method ExpectNodeProperties() implements the assertions depending on the configuration of its MoveTestArgs instance:

Executing our tests

We want our tests to be executed against the Microsoft implementation as well as our own implementation. The most straight-forward way of achieving this is to implement our tests in an abstract test fixture. The abstract fixture has an factory method to create an instance of XPathNavigator to test against. For each implementation, we create a subclass of our fixture and override the factory method.

CreateNavigable returns an IXPathNavigable. In turn IXpathNavigable lets us create a navigator positioned on the document root thanks to its CreateNavigator() method.

We’ll add the test fixture for our own implementation when we have the skeleton available. In the mean time, this lets us verify our expectations against the actual implementation of XPathNavigator.

The next post on the topic will tackle the new implementation’s design. I’ll make the implementation and test available as a source code download at the end of this series of articles.

Leave a Reply