Testing with XPathNavigator.Evaluate
Continuing from yesterday's post, we'll use XPathNavigator.Evaluate to sketch some helper functions that can be used to test components.
For example, if we're testing the results of a transformation or of some custom XML from a REST-ful service, we would probably want to make sure that some elements are where they're supposed to be and so on. We may not want to look for an exact match, either because there are things that may always change like a timestamp in the document, or because they are simply not relevant to what we're focusing on.
An easy way to do this testing is to simply write a list of XPath expressions that must all be true for a given document or section in a document. Because the Evaluate call can return different kinds of results, we should make sure we handle all of them. The following helper function does this.
public static void Assert(
IXPathNavigable node,
IXmlNamespaceResolver resolver,
string xpath) {
bool succeeded;
var navigator = node.CreateNavigator();
object result = navigator.Evaluate(xpath, resolver);
if (result is bool) {
// We'll succeed if the result is true.
succeeded = (bool)result;
} else if (result is double) {
// We'll succeed if the result is non-zero.
succeeded = ((double)result) != 0d;
} else if (result is string) {
// We'll succeed if the result is non-empty.
succeeded = !String.IsNullOrEmpty((string)result);
} else {
// We'll succeed if the result is non-empty.
XPathNodeIterator iterator = (XPathNodeIterator)result;
succeeded = iterator.MoveNext();
}
if (!succeeded) {
throw new Exception("Failed to match " + xpath);
}
}
If you're going to use this function, you should probably adapt this to throw a more appropriate exception, either for your runtime program or for your testing framework.
Now, let's add a short helper function.
public static void Assert(
IXPathNavigable node,
IXmlNamespaceResolver resolver,
IEnumerable<string> xpaths) {
foreach (var xpath in xpaths) {
Assert(node, resolver, xpath);
}
}
Now we're ready to write a short test for a document we create in some way or another.
// Create a document or get it from somewhere.
var doc = new XmlDocument();
doc.LoadXml("<root><p class='greeting'>Hello, world!</p></root>");
// Now do a few checks.
try {
Assert(doc, "/root");
Assert(doc, "/root/p");
Assert(doc, "/root/p/@class = 'greeting'");
Assert(doc, "/root/p[1]/text()");
// Oh noooo....
Assert(doc, "/root/p/@class = 'greetingz'");
} catch (Exception e) {
Console.WriteLine(e.Message);
}
If you run this, you'll see that the first expressions all work as expected, but the last one throws and causes the following to be written to the console.
Failed to match /root/p/@class = 'greetingz'
Enjoy!