.Net, MbUnit

MbUnit 3.2 makes it easier to work with Xml

In my previous article I mentioned about upcoming new release of Gallio /  MbUnit.  Feel free to download the Release Candidate and try it for yourself.  In this post I’ll talk about another new feature.

Not everybody like XML but everyone had to work with it. It’s used for many different purposes. Most people use it for configuration like web.config or .csproj / .vbproj. Some people use this format for data, others for communication. I believe that it’s going to be very hard to find a .NET developer that didn’t work with XML. If you do work with XML and you like to unit test your code, than I have good news for you. With new upcoming version 3.2 of MbUnit you are going to get better support to unit test your XML code. The Gallio team gives us three new Assert methods to test XML:

  • Assert.Xml.AreEqual
  • Assert.Xml.Exists
  • Assert.Xml.IsUnique

Assert.Xml.AreEqual

Let start by showing a simple and probably very obvious example:

        [Test]
        public void TestXmlAreEqualWithString()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            Assert.Xml.AreEqual(xmlString, XElement.Load(new StringReader(xmlString)).ToString());
        }

In the example above we’re comparing XML strings.  There’s another overload that instead of strings takes TextReader objects.

        [Test]
        public void TestXmlAreEqualWithTextReader()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            TextReader textReaderXml = new StringReader(xmlString);
            Assert.Xml.AreEqual(
                new StringReader("<Books><Book title='C# in Depth'/><Book title='Learing C#'/></Books>"),
                textReaderXml);
        }

As you can see, the formatting of XML is different, but our test still succeeds since it represents the same XML.

Let’s now make a small test.  We know that XML is case-sensitive  language.  What is going to happen if you we mismatch case?  Let’s make expected value for one of the book title ‘Learning c#’ instead of ‘Learning C#’.

        [Test]
        public void TestXmlAreEqualWithString()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            Assert.Xml.AreEqual(
                "<Books><Book title='C# in Depth'/><Book title='Learing c#'/></Books>",
                XElement.Load(new StringReader(xmlString)).ToString());
        }

After executing this test, we will see the following error that conveniently highlights the problem area:

AreEqual_error

Wait a second.  But what if in my application I don’t care about case-sensitivity for my data?  Well, in this case guys from the Gallio team provided us with an optional parameter of type XmlOptions.  Therefore, if we add XmlOptions.Loose parameter to our Assert.AreEqual, our test is going to pass.

            Assert.Xml.AreEqual(
                "<Books><Book title='C# in Depth'/><Book title='Learing c#'/></Books>",
                XElement.Load(new StringReader(xmlString)).ToString(), 
                XmlOptions.Loose);

You can find more information on XmlOptions in the Gallio Wiki site.

 

Assert.Xml.Exists

This assertion method checks if an element or an attribute can be found in a specific XML document.

  • The first parameter of Assert.Xml.Exists method is an Xml document.   Like in Assert.Xml.AreEqual method you can represent Xml document as a string or a TextReader object. 
  • The second parameter is an Xml Path that can be either a string in format “/element[/element]:attribute” or an object that implements IXmlPathLoose interface.
  • The third parameter is already familiar to us XmlOptions object.

Let’s look at a specific example where will check if Book element exists. 

        [Test]
        public void TestXmlExistsForElement()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            // Xml Path as a string
            Assert.Xml.Exists(xmlString, "/Books/Book", XmlOptions.Default);
            // Xml Path as a IXmlPathLoose
            Assert.Xml.Exists(xmlString, XmlPath.Element("Books").Element("Book"), XmlOptions.Default);
        }

For completeness let’s show a separate example where we will be looking for title attribute.

        [Test]
        public void TestXmlExistsForElement()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            // Xml Path as a string
            Assert.Xml.Exists(xmlString, "/Books/Book:title", XmlOptions.Default);
            // Xml Path as a IXmlPathLoose
            Assert.Xml.Exists(xmlString, XmlPath.Element("Books").Element("Book").Attribute("title"), XmlOptions.Default);
        }

In case an element or an attribute not found, Gallio will produce an error message similar to the one below:

Exists_error

Assert.Xml.IsUnique

This method takes the same parameters as Assert.Xml.ExistsAssert.Xml.IsUnique fails if an element is encountered more than once in Xml document.  I expect that you will not utilize this method as much as other two methods.

The following code is going to fail because we have two Book elements in out Xml.

        [Test]
        public void TestXmlIsUnique()
        {
            const string xmlString =
                "<Books>" +
                "  <Book title='C# in Depth' />" +
                "  <Book title='Learing C#' />" +
                "</Books>";

            // Xml Path as a string
            Assert.Xml.IsUnique(xmlString, "/Books/Book", XmlOptions.Default);
            // Xml Path as a IXmlPathLoose
            Assert.Xml.IsUnique(xmlString, XmlPath.Element("Books").Element("Book"), XmlOptions.Default);
        }

Here’s our error message:

IsUniqe_error

Last words.

I hope you find it useful and I didn’t waste mine and your time because my wife and kids never going get this hour back.

 Gallio wiki site is a great place to learn more about XML AssertionsGallio / MbUnit is an open source project and they always looking for new contributors.  If you consider joining the project, click here to learn how.

kick it on DotNetKicks.com

.Net, C#, Coding, Generics, XML

GetAttribute Generics way.

As developers we often need to work with Xml.  In my case I found myself writing code like this one:
   1: public void Main()
   2: {
   3:   string name;
   4:   bool required;
   5:   XmlDocument _xmlDoc = new XmlDocument();
   6:   _xmlDoc.LoadXml("<column name=\"colName\" required=\"true\"/>");
   7:   if (_xmlDoc.DocumentElement.HasAttribute("name"))
   8:     name = _xmlDoc.DocumentElement.GetAttribute("name");
   9:   if (_xmlDoc.DocumentElement.HasAttribute("required"))
  10:     required = Convert.ToBoolean(_xmlDoc.DocumentElement.GetAttribute("required"));
  11: }
I hated the fact that I have to for every attribute use two lines in my Main function.  Below you can see refactored code.
 
Note: You will need to pass XmlNamespaceManager if you’re using namespaces.
   1: public void Main()
   2: {
   3:   XmlDocument _xmlDoc = new XmlDocument();
   4:   _xmlDoc.LoadXml("<column name=\"colName\" required=\"true\"/>");
   5:   string name = GetAttribute<string>(_xmlDoc.DocumentElement, "name");
   6:   bool required = GetAttribute<bool>(_xmlDoc.DocumentElement, "required");
   7: }
   1: private T GetAttribute<T>(XmlElement columnElement, string attributeName)
   2:   where T : IConvertible
   3: {
   4:   if (columnElement.HasAttribute(attributeName))
   5:     return (T) Convert.ChangeType(columnElement.GetAttribute(attributeName), typeof (T));
   6:   return default(T);
   7: }
I hope you’ll find it useful.

kick it on DotNetKicks.com