How To Unit Test – Linq To XML

Now that I’m back from TechEd, it’s time to continue my series of “How To Unit Test” posts.  In this post, I’ve decided to have a quick look into Linq to XML and how that could be unit tested.  I’ve decided to pick the nice standard sample for Linq to XML – A Rss Reader :).  This also follows on nicely from my previous post on How To Unit Test Using SubSonic and Rhino Mocks

Unit testing Linq to Xml is no great big secret. The fact that you are performing Linq queries under the covers doesn’t really affect your unit tests, but it’s still quite interesting.

To start with, I’m going to be using the BBC News feed as my sample xml feed to query. My first test is to return an RssChannel object which had a url and title.  Note, I’m not implementing all the properties as that would get very boring very quickly.

[Test]
public void GetRssChannel_StandardRss_ReturnDataRssChannel()
{
    RssProcessor rss = new RssProcessor();
    RssChannel chan = rss.GetRssChannel(TestHelper.url);
    Assert.AreEqual(TestHelper.url, chan.Url);
    Assert.Contains(chan.Title, “BBC”);
}

The implement on the method then simple queries a XDocument, creates my RssChannel object and returns it to the caller.

public RssChannel GetRssChannel(string url)
{
    XDocument x_Feed = XDocument.Load(url);
    var feeds = from f in m_Feed.Elements(“rss”).Elements(“channel”).Take(1)
                       select new RssChannel
                       {
                           Title = f.Element(“title”).Value,
                           Url = m_Url
                       };

    return feeds.First();
}

The next method does a similar function but returns a RSSItem for the position in the document (Linq to XML is great for doing queries like this).

[Test]
public void GetRssItem_RssFeed_ReturnsASingleRSSItem()
{
    RssProcessor rss = new RssProcessor();
    RssItem item = rss.GetRssItem(TestHelper.url, 1);
    Assert.IsNotNull(item);
    Assert.IsNotNull(item.Title);
}

The query would be:

var feeds = from f in m_Feed.Elements(“rss”).Elements(“channel”).Elements(“item”).Skip(itemPosition – 1).Take(1)
            select new RssItem
            {
                Title = f.Element(“title”).Value
            };

Instantly we can see from the tests that we can do a bit of refactoring.  Firstly, we can move the creation of the RssProcessor into a [Setup] method.  We can also give the Url for the feed as a parameter in the constructor.  This makes our test code cleaner as there is less duplication, but also more readable as we are focusing more on the intent of the tests.

The complete solution can be downloaded at the end of the post, however there is one more test which I want to refer to.

The following returns an RssFeed object which has a reference to the RssChannel and a List.

[Test]
public void GetRssFeed_EntireFeed_ReturnsChannelAndItems()
{
    RssFeed feed = m_Rss.GetFeed();
    Assert.IsNotNull(feed.Channel);
    Assert.Contains(feed.Channel.Title, “BBC”);
    Assert.GreaterThan(feed.Items.Count,0);
}

The test works fine, however based on the implementation on the existing tests, each method loads their own Rss feed/XDocument. This is great for isolation, but doesn’t work well when you put it all together as loading a single feed will cause it to actually be loaded three times – that simply doesn’t scale.

The solution was to have a LoadFeed method on the RssProcessor which would be called before GetFeed().  This could also allow for a single object to be used for returning multiple feeds.  Note for future: My first test should really have been for this method to make sure I can actually receive an object to work with before querying it.

m_Rss.LoadFeed(TestHelper.url);

Having this single method as the way to load and return the Xml makes mocking very easy. Following the approaches in previous posts, I created myself an IFeedGetter object, with a property to injection a mock if required on my RssProcessor.  The load method then simply passes it’s call onto the IFeedGetter object.

public XDocument LoadFeed(string url)
{
    m_Url = url;
    m_Feed = m_FeedGetter.GetXDocument(url);
    return m_Feed;
}

I can then create a stub feed getter to be used during unit testing.

public class StubFeedGetter : IFeedGetter
{
    public System.Xml.Linq.XDocument GetXDocument(string url)
    {
        return XDocument.Load(System.Xml.XmlReader.Create(new System.IO.StringReader(GetFakeXml())));
    }
}

So that’s it,  we have now unit tested our complete Rss implementation using mocks and Linq to XML!  If you need support in getting this to work, please give me a shout.

Download Solution: HowToUnitTest_MockLinq-ToXml.zip

Technorati Tags: , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *