How To Unit Test – RegEx and Technorati

Following on from my previous post, I wanted to implement another feature in the blog application. The feature is to pick out the Technorati tags from a post and pull relates posts from the Technorati API.  I’ve already blogged about how to process the Technorati API.

To implement this feature, the first thing i’m going to have implement is being able to return a list of all the tags for a post.

I found unit testing RegEx really difficult.  I’m not great at RegEx as I only rarely have a requirement to do it and this is where unit testing becomes difficult.  Its easy to unit test code you have wrote before but unit testing code you haven’t done in a while takes a little more effort.  I feel the reason is because you are thinking about the design in your head to try and figure out how it all fits together.  During this implementation, I found the best thing to do was simply just start writing simple code and spike RegEx.  To spike RegEx I wrote a simple test which I thought should pass successfully based on my previous.

[Test]
public void RegExMatchesString()
{
    Regex r = new Regex(“http://technorati.com/tags/(.+?) rel=”tag”>(.+?)“);
    MatchCollection collection =
        r.Matches(“http://technorati.com/tags/TestCode” rel=”tag”>Test Code“);

    Assert.AreEqual(1, collection.Count, “Collection”);

    foreach (Match match in collection)
    {
        Assert.IsTrue(match.Success);
        Assert.AreEqual(2, match.Captures.Count, “Captures”);
        Assert.AreEqual(“TestCode”, match.Captures[0].Value, “One”);
        Assert.AreEqual(“Test Code”, match.Captures[1].Value, “Two”);
    }
}

This fails, so I knew I must have done something wrong. I then jumped quickly into the debugger, and found instead of using the Captures collection, I wanted the Groups collection. I retested it and found Groups actually includes both the two matches plus the whole thing. The whole thing is at 0 index in the collection so I had to move everything alone one.  A couple of minutes later I had my regex code working.  There’s nothing like seeing a test go green!  The actual implementation is below.

[Test]
public void Spike_RegExMatchesString()
{
    Regex r = new Regex(“http://technorati.com/tags/(.+?) rel=”tag”>(.+?)“);
    MatchCollection collection =
        r.Matches(“http://technorati.com/tags/TestCode” rel=”tag”>Test Code“);

    Assert.AreEqual(1, collection.Count, “Collection”);

    foreach (Match match in collection)
    {
        Assert.IsTrue(match.Success);
        Assert.AreEqual(3, match.Groups.Count, “Captures”);
        Assert.AreEqual(“TestCode”, match.Groups[1].Value, “One”);
        Assert.AreEqual(“Test Code”, match.Groups[2].Value, “Two”);
    }
}

Based on the code completed during the spike, I can quickly test and implement the code by refactoring out the important parts into actual unit tested methods.  Spending that bit extra time to look into the spike made the actual implementation a lot quicker and easier.

[Test]
public void GetMatchCollectionForHtml()
{
    string post = “http://technorati.com/tags/TestCode” rel=”tag”>Test Code“;
    MatchCollection collection = Technorati.GetMatches(post);
    Assert.AreEqual(1, collection.Count, “Collection”);
}

[Test]
public void GetListFromMatches()
{
    string post = “http://technorati.com/tags/TestCode” rel=”tag”>Test Code“;
    MatchCollection collection = Technorati.GetMatches(post);

    List tags = Technorati.GetTagList(collection);
    Assert.AreEqual(1, tags.Count);
    Assert.AreEqual(“TestCode”, tags[0]);
}

Once they work, I write a test to ensure it works together with ‘real’ data.

[Test]
public void GetTagListFromPostText()
{
    RssFeed rss = TestHelper.CreateMockRSS(TestHelper.url);
    RssItemCollection items = rss.Channels[0].Items;

    string post = items[0].Description;
    List tags = Technorati.GetTagListFromPost(post);
    Assert.AreEqual(3, tags.Count);
    Assert.AreEqual(“MbUnit”, tags[0]);
    Assert.AreEqual(“TDD”, tags[1]);
    Assert.AreEqual(“Testing”, tags[2]);
}

Great, that’s our tag list completed.  We now just need to pull the posts from Technorati.

My previous post wasn’t very testable, the method was doing a lot.  It was pulling data, processing the feeds and returning a list. This version has been separated into a lot smaller methods to make unit testing easier and more effective.  The first test is to make sure we can return a list of XmlNodes from the API.

[Test]
public void GetXmlFromRss()
{
    int limit = 10;
    string technoratiTerm = “MbUnit”;

    XmlNodeList xmlNodes = Technorati.GetXmlNodeList(Technorati.GetUrl(technoratiTerm));
    Assert.AreEqual(limit, xmlNodes.Count);
}

We then want to make a Blog object from one of the XmlNodes in the list.

[Test]
public void GivenXmlNodeCreateBlog()
{
    string technoratiTerm = “MbUnit”;

    XmlNodeList xmlNodes = Technorati.GetXmlNodeList(Technorati.GetUrl(technoratiTerm));
    Blog blog = Technorati.CreateBlogPost(xmlNodes[0]);
    Assert.AreEqual(blog.Title, xmlNodes[0][“title”].InnerText);
}

After that has been implemented, we can can a list of Blog objects from a list of nodes (it simply calls the above method multiple times).

[Test]
public void GetBlogPostListTerm()
{
    string technoratiTerm = “MbUnit”;

    XmlNodeList xmlNodes = Technorati.GetXmlNodeList(Technorati.GetUrl(technoratiTerm));
    List blogs = Technorati.CreateBlogPostList(xmlNodes);
    Assert.AreEqual(10, blogs.Count);
}

Finally, we can get a list of blog posts for a list of strings (again, calling the above method multiple times).

[Test]
public void GetBlogPostListForListOfTerms()
{
    List terms = new List(3);
    terms.Add(“MbUnit”);
    terms.Add(“TDD”);
    terms.Add(“Testing”);

    List blogs = Technorati.CreateBlogPostList(terms);
    Assert.AreEqual(30, blogs.Count);
}

The only problem with these tests is that they are hitting the Technorati API for each test.  It would be much better if we could abstract away from the API and use a stub object instead. A stub object allows us to replace the implementation however are they not as cleaver as a mock object and more limited, but for this simple task it will fit the purpose as using a full mock framework isn’t really required and similar the better really.

To do this, I’m going to inject the stub object the same way I would a mock object by using a static method on the Technorati object.  Within my test code, I use TechnoratiStub (Implemented within my test assembly, no test code inside the actual assembly under test).  In the real system I use TechnoratiAccessor.

Technorati.TechnoratiAPI = new TechnoratiStub();

private static ITechnoratiAccessor dataStore = new TechnoratiAccessor();
public static ITechnoratiAccessor TechnoratiAPI
{
    get { return dataStore; }
    set { dataStore = value; }
}

I then move the implementation of hitting the API and returning the XmlNodeList to the TechnoratiAccessor.

public interface ITechnoratiAccessor
{
    XmlNodeList GetXmlNodeList(string technoratiUrl);
}

public class TechnoratiAccessor : ITechnoratiAccessor
{
    public XmlNodeList GetXmlNodeList(string technoratiUrl)
    {
        XmlDocument xmlResultsSet = new XmlDocument();
        xmlResultsSet.Load(new XmlTextReader(technoratiUrl));
        XmlNodeList xmlResponse = xmlResultsSet.SelectNodes(“/tapi/document/item”);
        return xmlResponse;
    }
}

Then, in my Technorati object, I just return whatever the TechnoratiAPI gives me (be it the real implementation or the stub).

public static XmlNodeList GetXmlNodeList(string technoratiUrl)
{
    return TechnoratiAPI.GetXmlNodeList(technoratiUrl);
}

All of our tests still pass, so we can now implement the stub.  In all the tests I setup the property to point to the Stub and then the stub just loads a valid XML response I have cached in a string to a XmlDocument and return the XmlNodeList. Very quick and easy.

public class TechnoratiStub : ITechnoratiAccessor
{
    public XmlNodeList GetXmlNodeList(string technoratiUrl)
    {
        #region Xml
        string cachedXmlResponse = @”Too long to insert here”;
        #endregion

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(cachedXmlResponse);
        return xmlDoc.SelectNodes(“/tapi/document/item”);
    }
}

Now when all of our tests execute, they use this Stub object instead of the real API.  Hope you find this useful.

Downloads: TechnoratiTests.cs  ||  Technorati.cs  (These include all of the discussed in this post).

Technorati Tags: , , , ,

How To Unit Test – Using SubSonic and Rhino Mocks

“All problems in computer science can be solved by another level of indirection” Butler Lampson (Or Abstraction as some people refer to it as). 

This is one of the most used quotes within computer science and when it comes to talking about mock objects I think it is particularly true. 

As discussed in Part 1 and Part 2 of my articles on Rhino Mocks, attempting to mock part of the .Net framework is not worth the effort and it is better if you create a wrapper around the framework implementation and then mock the wrapper (adding another layer of abstraction).

In this post, I will outline one possible approach to using mock objects and Rhino Mocks with SubSonic.  There are other approaches, mainly focusing around MVC, however for this approach I want to keep it focusing on using SubSonic as a DAL without discussing MVC.

Scenario

In this post, I’m going to implement a rss aggregator/roller.  It will check blog posts, save any new posts into a database and then the application will pull the blog posts out of the database. 

The flow of data will be this:

RSS > Post Manager > Database > Post Manager ||  Application.

For the moment, I’m not interested in the UI, I will be building the API which could easily be used by an application.  The reason why I picked a blog aggregator is because it provides a interesting TDD/Unit Testing challenge.  Not only are we dealing with databases, which as we found out is difficult, but we are also pulling data from a 3rd party source via the internet.

Implementation

Let’s start implementing the solution.  For a RSS Aggregator, the most important thing really is to take a RSS Feed and process the contents.  Because we are using mocks, we can mock out the external RSS feed and just use a known valid response, at runtime this will be replaced with code which makes a call to the web server.  The most important thing to remember is to mock at the external boundary as its at this point we lose control over the execution.

Important considerations for the implementation

Before we start, there are some important considerations about the implementation which we should think about before writing tests/code.  One of the main things to remember is to keep everything isolated and focus on the task in hand, we need to make sure our methods and tests aren’t going off and breaking their boundaries.  When we are testing the Post Manager for obtaining the RSS Feeds, we don’t want to be hitting the database to see if it saves correctly as well. Next, we want to be sure we are mocking the correct object in the system.  There is no point mocking the Rss object if all it does is hold data, we want to be mocking the object directly related to the OUT (Object Under Test) which has it’s own set of related tests to ensure it works correctly.  The objects we generally want to mock out are objects which interact with external dependences, in this case the RssFeed on the web and the database.

Finally, we want to make sure that our tests are running independently of each other and cleaning up after themselves if they make any permanent charges.

With that said, The first requirement for the system is be able to get the xml for a url.

[Test]
public void GetXmlFromURL()
{
    RssFeed rss = PostManager.GetRssFeed(http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml);
    Assert.AreEqual(TestHelper.url, rss.Url);
}

The test simply says we want to access a static method on the PostManager called GetRssFeed and by giving it a url, we can return an RssFeed object. For my RSS objects, I will be using RSS.Net to save me implementing and testing the feed parsing (why build it when its already done).  I actually found this framework very easy to use and completely open source so you can modify it as you see fit (I did to make RssFeed.Url not read-only).  To implement the method on the PostManager, we call a method on the Rss.Net object.

public Rss.RssFeed GetRssFeed(string url)
{
    return RssFeed.Read(url);
}

Now we have a working implementation, it would be nice if we could mock obtaing the RSS instead of having to hit the rss feed every time we wanted to test the method.

[Test]
public void MockGettingRssFeed()
{
    //This is what we want our mock object to return to the calling code.
    RssFeed rss = TestHelper.CreateMockRSS(TestHelper.url);
    MockRepository mocks = new MockRepository();
    IFeedGetter feedGetter = mocks.CreateMock();

    using (mocks.Record()) //Setting up what we expect the mock object to do.
    {
        Expect.Call(feedGetter.GetFeed(TestHelper.url)).Return(rss);
    }

    using (mocks.Playback()) //Performing the test
    {
        PostManager.FeedGetter = feedGetter;
        RssFeed actualRss = PostManager.GetRssFeed(TestHelper.url);

        Assert.GreaterThan(rss.Channels.Count, 0);
        Assert.AreEqual(TestHelper.url, rss.Channels[0].Link.AbsoluteUri);
    }
}

In order for obtaining the RSS feed to be mocked out, I moved the GetFeed method into a IFeedGetter interface (implemented as FeedManager) and added a FeedGetter property to the PostManager object.  This allows me to use the default object (FeedManager) in the real system without changing the code, however during unit testing (or in the future) I can replace the default implementation with my own – in this case a mock IFeedGetter.

private static IFeedGetter feedGetter = new FeedManager();
public static IFeedGetter FeedGetter
{
    get { return feedGetter; }
    set { feedGetter = value; }
}

In order to keep the test code cleaner, I extracted the creation of the mock RssFeed object into a TestHelper method, this can then be called from multiple locations within my test code without having to manually create an object each time.  There is also a unit test associated with this method to make sure it works correctly.

Our implementation of FeedGetter and PostManager is complete.  Using Rss.Net allowed us to cut the implementation time.  Moving on, we need to be able to save this RssFeed object into the database. 

If you are not aware, an RSS is made up of general information about the feed (RssChannel) and then a number of items (RssItem).  To save the feed, the first thing we need to do is save the RssChannel object.

[Test]
public void InsertFeedDetailsIntoDatabase()
{
    using (mocks.Record())
    {
        Expect.Call(database.InsertFeedDetails(rssFeed.Channels[0])).Return(1);
    }

    using (mocks.Playback())
    {
        int recordID = DatabaseManager.InsertFeedDetails(rssFeed.Channels[0]);

        Assert.GreaterThan(recordID, 0);
    }
}

This method simple says that a method on the database object should accept an RssChannel object and return a 1 if its inserted correctly.  We them access this method via a DatabaseManager object.  For the test to be shorter, I moved a lot of the code into the [Setup] for the test fixture.

RssFeed rssFeed;
MockRepository mocks;
IDataStore database;

[SetUp]
public void TestSetup()
{
    rssFeed = TestHelper.CreateMockRSS(TestHelper.url);
    mocks = new MockRepository();
    database = mocks.CreateMock();
    DatabaseManager.DataStore = database;
}

Notice I’m setting the DataStore of the DatabaseManager to the IDataStore mock object via a property. One thing which I want to test for is to make sure that if the property hasn’t been setup correctly, then the correct exception is thrown.  This just saves any nasty surprises later.

[Test]
[ExpectedArgumentNullException]
public void ThrowExceptionOnSaveFeedIfNoDataSource()
{
    DatabaseManager.DataStore = null;
    DatabaseManager.SaveFeed(rssFeed);
}

Moving on, the next item to implement is saving an actual RssItem.   The test just makes sure that the InsertFeedItem is called twice, once for each RssItem in the collection.

[Test]
public void InsertFeedItemsIntoDatabase()
{
    int channelId = 1;
    using (mocks.Record())
    {
        Expect.Call(database.InsertFeedItem(channelId, rssFeed.Channels[0].Items[0])).Return(1);
        Expect.Call(database.InsertFeedItem(channelId, rssFeed.Channels[0].Items[1])).Return(2);
    }

    using (mocks.Playback())
    {
        bool result = DatabaseManager.InsertFeedItems(channelId, rssFeed.Channels[0].Items);
        Assert.IsTrue(result);
    }
}

Now we have both parts to save the feed in place, we can write another method which tests we can do both.

[Test]
public void InsertFeedIntoDatabase()
{
    int channelID = 1;

    using (mocks.Record())
    {
        Expect.Call(database.InsertFeedDetails(rssFeed.Channels[0])).Return(channelID);
        Expect.Call(database.InsertFeedItem(channelID, rssFeed.Channels[0].Items[0])).Return(1);
        Expect.Call(database.InsertFeedItem(channelID, rssFeed.Channels[0].Items[1])).Return(2);
    }

    using (mocks.Playback())
    {
        bool result = DatabaseManager.SaveFeed(rssFeed);
        Assert.IsTrue(result);
    }
}

Now our DatabaseManager is setup correctly with the methods we want to expose to the public which meet the requirements, we can move onto implementing our IDataSource which will actually store the information.  For this, we are using SubSonic to save all of our Rss information into the database.

The first problem we face is converting our Rss.Net objects into the ActiveRecord SubSonic objects for the database, a method is required to covert between the two objects. Inside of SubSonicTests.cs, we have a test to make sure this is done correctly on our dataSource object.

[Test]
public void ConvertFromRssChannelToSubSonicRssChannel()
{
    RssChannel rssChannel = rss.Channels[0];
    //Returns a SubSonic object.
    MockSubSonic.DAL.RssChannel subSonicChannelObject = dataSource.ConvertRssChannelObject(rssChannel);

    Assert.AreEqual(rssChannel.Title, subSonicChannelObject.Title);
    Assert.AreEqual(TestHelper.url, rssChannel.Link.AbsoluteUri);
    Assert.AreEqual(rssChannel.Link.AbsoluteUri, subSonicChannelObject.Uri);
    Assert.AreEqual(rssChannel.Description, subSonicChannelObject.Description);
    Assert.AreEqual(rssChannel.PubDate, subSonicChannelObject.PubDate);
    Assert.IsTrue(subSonicChannelObject.IsNew); //it shouldn’t save it.
}

We can then implement our solution to save the details into the database, first the details:

[Test]
public void InsertRssChannelIntoDatabase()
{
    int channelId = dataSource.InsertFeedDetails(rss.Channels[0]);
    Assert.AreEqual(channelId, 1);
}

Then the RssItems.

[Test]
public void InsertRssItemIntoDatabase()
{
    int channelId = dataSource.InsertFeedDetails(rss.Channels[0]);
    Assert.GreaterEqualThan(channelId, 0);

    int itemId = dataSource.InsertFeedItem(channelId, rss.Channels[0].Items[0]);
    Assert.AreEqual(itemId, 1);
}

We are not using mock objects at this point because we have refactored to the lowest reasonable point.  Any more and it gets too confusing and becomes pointless. We have cut a lot of the database access out by making it mockable in the first place, having these methods hit the database isn’t going to be a great problem.  So we don’t get any problems in the future, we need to make sure we clean up after ourselves.

[SetUp]
public void TestSetup()
{
    rss = TestHelper.CreateMockRSS(TestHelper.url);
    dataSource = new SubSonicDataStore();
    ExecuteSql(“TRUNCATE TABLE RssChannel”);
    ExecuteSql(“TRUNCATE TABLE RssItem”);
}

[TearDown]
public void TestTearDown()
{
    rss = null;
    dataSource = null;
    ExecuteSql(“TRUNCATE TABLE RssChannel”);
    ExecuteSql(“TRUNCATE TABLE RssItem”);
}

This is the bases for the rest of the implementation of the database.  We use SubSonic within the DataSource without any tricks or mock objects to ensure its working correctly, however in any later which isn’t interacting directly with the database, we are using mock objects.  The other part of the system which I will cover is getting information from the database.

The test below is to ensure that our DataManager object can return RssItems for a Channel based on the ChannelID.

[Test]
public void GetFeedItemsForChannelFromDatabase()
{
    int channelId = 1;
    using (mocks.Record())
    {
        Expect.Call(database.GetRssItemsForChannel(channelId)).Return(rssFeed.Channels[0].Items);
    }

    using (mocks.Playback())
    {
        RssItemCollection rssFeeds = DatabaseManager.GetRssFeedsForChannel(channelId);
        Assert.IsNotNull(rssFeeds);
        Assert.AreEqual(2, rssFeeds.Count);
    }
}

The SubSonic test for this requirement is below.  To start with we are inserting the channel and two feeds from our mock object into the database so we can actually return something.  We then obtain the collection from our database and to ensure all the feeds we got are correct we are verifying them within a foreach loop against our known TestHelper Rss Mock object which we inserted.

[Test]
public void GetRssChannelFeeds()
{
    int channelId = dataSource.InsertFeedDetails(rss.Channels[0]);
    dataSource.InsertFeedItem(channelId, rss.Channels[0].Items[0]);
    dataSource.InsertFeedItem(channelId, rss.Channels[0].Items[1]);

    RssItemCollection rssColl = dataSource.GetRssItemsForChannel(channelId);
    Assert.AreEqual(2, rssColl.Count);

    int i = 0;
    foreach (RssItem item in rssColl)
    {
        Assert.AreEqual(rss.Channels[0].Items[i].Title, item.Title);
        Assert.AreEqual(rss.Channels[0].Items[i].PubDate.ToString(), item.PubDate.ToString());
        Assert.AreEqual(rss.Channels[0].Items[i].Link.AbsoluteUri, item.Link.AbsoluteUri);
        Assert.AreEqual(rss.Channels[0].Items[i].Description, item.Description);
        Assert.AreEqual(rss.Channels[0].Items[i].Author, item.Author);
        i++;
    }
}

The advantage to inserting all the information at the start of the test is that we know what is in the database and what to expect when its returned.  If we relied on the contents of the database, then other tests could change the values or someone could come along and clean out the database – our tests would then fail as what we expected wasn’t what was actually their. This makes debugging the failure even more difficult as we have to investigate why it failed as it might not be because of our implementation but conflicting test runs.

If your interested in how the rest of the system is implemented, please download the project solution at the bottom of the post.

Passing Tests

After we have implemented our API, we have a number of tests which should all pass green.

MockSubSonicPassing

MockSubSonicCoverage

The reason we haven’t got 100% code coverage is because one or two lines of error handling I inserted which wasn’t tested against (for example, if a user couldn’t be found).  In a real system, we would need to test against these situations.  I have exclude the code generated by SubSonic and the RSS.Net project from the report, as that’s not directly related to the system and doesn’t provide a good indication of our unit test coverage.

To actually make sure it all works correctly, I created a simple Console Application which calls all the methods.

static void Main(string[] args)
{
    Console.WriteLine(“Welcome UserA to MockSubSonic”);
    User u = new User(“UserA”, “PassA”, “[email protected]”);
    DatabaseManager.SaveUser(u);

    Console.WriteLine(“Saving Feed”);
    DatabaseManager.SaveFeed(RssFeed.Read(“
http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml”));

    Console.WriteLine(“Subscribing”);
    bool result = DatabaseManager.SubscribeUserToFeed(u.Name, “
http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml”);

    if(result)
        Console.WriteLine(“You successfully subscribed to
http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml”);
    else
        Console.WriteLine(“You failed to subscribed to
http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml. Sorry”);

    RssFeedCollection feeds = DatabaseManager.GetUsersRssFeedCollection(u.Name);
    Console.WriteLine(“You are now subscribed to: ” + feeds.Count);
    Console.WriteLine(“First one is : ” + feeds[0].Channels[0]);

    Console.ReadLine();
}

image

Another way of doing this would have been a more Integrated style test using MbUnit.  It’s the kind of the same as the tests we have wrote before, however it touches more/all parts of the system to verify it is working correctly together.

Summary

I hope you have found this post useful and interesting if you are looking at SubSonic and mock objects.  The important thing to remember when mocking SubSonic is to wrap the SubSonic object inside your own access layer (in this case SubSonicDataStore : IDataStore).

Feedback/comments welcome!

Resources:

Code – Unit Tests, API, Application, SQL zip

SubSonic

Rhino Mocks

MbUnit

Rss.Net

Technorati Tags: , , , ,

How To Unit Test – Using SubSonic

In my previous article I wrote about how to unit test with the database.  However, SubSonic can do all of that work for us.  If you haven’t used it before, SubSonic is a great Object Relational Mapping Framework based on ActiveRecord (same as Ruby on Rails) for .Net 2.0. Simply point it at a database and it will create your all the code for connecting to your database. But once you have this code, how can you test with it?

However, just because SubSonic is a nature and well tested framework it doesn’t mean that you can trust the code it creates as it still needs to be tested.  But don’t expect 100% code coverage!  You will still want to test that the parts of subsonic you are using are actually working correctly. You shouldn’t find any errors, but you ever known!

Scenario

In this post I’m going to create a simple application to insert customers into our database via a console application and also have a list of customers printed out to the console. Similar to the previous post.

The application will be separated into three parts. SubSonic will be used for all the database access, a middle layer will be the ‘glue’ and perform validation on the input (this could be business rules) and will sit on top of subsonic classes.  This will then be accessible from the console application (but it shouldn’t take much to change this to a WinForm/WebForm application).  I know it’s simple (and a bit uninteresting) but I just want to demonstrate the concepts more than anything else.

Unit Tests

The first thing to do is create the database and generate the classes for the database.  I spoke about how to setup SubSonic in a previous post.

Next, I want to be able to insert a customer.

[Test]
public void CreateCustomer()
{
    string name = “John Smith”;
    string email = “[email protected]”;
    Customer c = CustomerManager.CreateCustomer(name, email);
    Assert.AreEqual(name, c.Name);
    Assert.AreEqual(email, c.Email);
}

Here, I have created a CustomerManager static class to provider a layer above the SubSonic implementation. 

I then return a SubSonic generated Customer object.  This is actually really bad as we are returning an object of type ActiveRecord with a lot of additional methods which we wouldn’t want other layers to access (like .Save()).  By doing this, we also need to reference the SubSonic assembly within our test framework, again this is really bad if you want a good separation of the system as we have become dependent on SubSonic.  To stop this, i’m actually going to return our own Customer object to high-layers with just the information we want to expose, we lose some of the advantages of SubSonic but I can accept that for the abstraction. For the moment, it will be just Name and Email.

[Test]
public void CreateCustomer()
{
    string name =  “Customer Test”;
    string email = “[email protected]”;
    Customer c = new Customer(name, email);
    Assert.AreEqual(name, c.Name);
    Assert.AreEqual(email, c.Email);
}

Our CustomerManager implementation then looks like this:

public static Customer CreateCustomer(string name, string email)
{
    HowToUnitTestDB.Customer c = new HowToUnitTestDB.Customer();
    c.Name = name;
    c.Email = email;
    c.Save();

    return new Customer(c.Name, c.Email);
}

Here we are creating two objects, but I feel a lot more conformable with this level of expose and flexibility. If we change to Linq to SQL tomorrow, our higher layers won’t care.

Back to the Tests.  Next, I want to ensure that CustomerManager does correct validation on the fields to ensure no bad data is entering our system.

[Test]
[ExpectedException(typeof(Exception))]
public void CreateNullCustomer()
{
    Customer c = CustomerManager.CreateCustomer(null, null);
    Assert.Fail(“Should not get here”);
}

That’s OK, but we are relying on SubSonic doing the validation, hence why we are expecting a System.Exception to be thrown.  Personally, I would prefer a ArgumentNullException to be thrown instead and I would also prefer CustomerManager to be doing the validation.

[Test]
[ExpectedArgumentNullException]
public void CreateNullCustomerWithOutValidation()
{
     Customer c = CustomerManager.CreateCustomer(null, null);
     Assert.Fail(“Should not get here”);
}

That feels better, notice how i’m using a cool MbUnit attribute to save a little bit of typing and make it more readable.

Next, I want to be able to return a Customer from the database based on their name. If the customer does not exist, I want an ArgumentException to be thrown.  I’m using MbUnit RowTest feature – Read more on this here.

[RowTest]
[Row(“John Smith”)]
[Row(“Unknown Customer”, ExpectedException = typeof(ArgumentException))]
public void GetCustomer(string name)
{
    CustomerManager.CreateCustomerWithoutValidation(“John Smith”, “”);
    Customer c = CustomerManager.GetCustomer(name);
    Assert.AreEqual(name, c.Name);
}

Now I can return one customer, I want to be able to return all the customers as a list.

[Test]
public void GetAllCustomers()
{
    string name = “John Smith”;
    CustomerManager.CreateCustomerWithoutValidation(name + ” A”, “”);
    CustomerManager.CreateCustomerWithoutValidation(name + ” B”, “”);
    CustomerManager.CreateCustomerWithoutValidation(name + ” C”, “”);
    List customers = CustomerManager.GetAllCustomers();
    Assert.AreEqual(3, customers.Count);
}

With the implementation looking like this:

public static List GetAllCustomers()
{
    SqlDataReader dr = (SqlDataReader)HowToUnitTestDB.Customer.FetchAll();

    List custs = new List();

    while (dr.Read())
    {
        Customer c = new Customer();
        c.Name = dr.GetString(1);
        c.Email = dr.GetString(2);
        custs.Add(c);
    }

    return custs;
}

While this test looks fine it does expose a problem with the tests as a whole and that is that we are not cleaning up after ourselves, like with the last post.  This is one of the problems with database testing as you need to ensure the tests don’t leave lasting data which can have knock on effects.

Executing the test, we receive this error message.

Error    1    TestCase ‘CustomerManagerTests.GetAllCustomers’ failed:  Equal assertion failed: [[3]]!=[[47]]
MbUnit.Core.Exceptions.NotEqualAssertionException
Message:  Equal assertion failed: [[3]]!=[[47]]
Source: MbUnit.Framework
StackTrace:
   at MbUnit.Framework.Assert.FailNotEquals(Object expected, Object actual, String format, Object[] args)
   at MbUnit.Framework.Assert.AreEqual(Int32 expected, Int32 actual, String message)
   at MbUnit.Framework.Assert.AreEqual(Int32 expected, Int32 actual)
   at SubSonic.Tests.CustomerManagerTests.GetAllCustomers() in E:UsersBen HallDocumentsVisual Studio 2008ProjectsHowToUnitTestSubSonic.TestsCustomerManagerTests.cs:line 69    E:UsersBen HallDocumentsVisual Studio 2008ProjectsHowToUnitTestSubSonic.TestsCustomerManagerTests.cs    69
   

To solve this, I will use the SetUp and TearDown fixtures, however I don’t really want a method to delete the customers on my CustomerManager ( CustomerManager.DeleteAllCustomers();) just to be able to clean up my test code.

[SetUp]
public void TestSetup()
{
    DeleteCustomers();
}

[TearDown]
public void TestTearDown()
{
    DeleteCustomers();
}

private static void DeleteCustomers()
{
    SqlConnection sqlConn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings[“DatabaseConnection”].ConnectionString);
    SqlCommand sqlCmd = new SqlCommand(“TRUNCATE TABLE dbo.Customer”, sqlConn);

    try
    {
        sqlConn.Open();

        sqlCmd.ExecuteNonQuery();
    }
    finally
    {
        if (sqlConn.State == System.Data.ConnectionState.Open)
            sqlConn.Close();
    }
}

This is a bit naughty as we are accessing the database directly, however I feel it is the better of two evils in this situation, the more complex your data becomes maybe you would rethink, or if you already had a DeleteAllCustomers() method you could use that,  it’s just that in this case at the moment we don’t.

Hopefully by now your getting a good feel for unit testing with SubSonic, sadly we don’t mock anything but it doesn’t mean you can’t do it…  One final thing I want to talk about is verifying updates to a customer.

To update a customer, we need to know their ID.  At the moment, our public customer object doesn’t expose this information, however we can quickly modify this.

[Test]
public void GetCustomerID()
{
    string name = “John Smith”;
    CustomerManager.CreateCustomerWithoutValidation(name, “”);

    Customer c = CustomerManager.GetCustomer(name);

    Assert.AreEqual(1, c.ID);
}

In order for this to build, I need to add a ID property on the Customer object

public int ID { get; internal set; }

I can then modify my GetCustomer method to return the ID as well. All my previous tests ensure I haven’t broken anything along the way.  We can now write a correct test.

[Test]
public void UpdateCustomer()
{
    string name = “John Smith”;
    CustomerManager.CreateCustomerWithoutValidation(name, “”);
    Customer c = CustomerManager.GetCustomer(name);
    Assert.AreEqual(name, c.Name);

    c.Email = “[email protected]”;

    CustomerManager.UpdateCustomer(c.ID, c);
}

After that, the passing implementation looks like this:

public static void UpdateCustomer(int customerID, Customer c)
{
    HowToUnitTestDB.Customer dbCust = HowToUnitTestDB.Customer.FetchByID(customerID);
    dbCust.Name = c.Name;
    dbCust.Email = c.Email;
    dbCust.Save();
}

We can now insert, get and update customers.  Great! We just need to put a UI on it.  A simple console application will do:

class Program
{
        [STAThread]
        public static void Main(string[] args)
        {
            Console.WriteLine(“How To Unit Test: SubSonci”);
            string command = GetCommand();

            while (command != “Quit”)
            {
                switch (command)
                {
                    case “Insert”:
                        InsertCustomer();
                        break;
                    case “Get”:
                        GetCustomer();
                        break;
                    case “Update”:
                        UpdateCustomer();
                        break;
                    default:
                        break;
                }

                command = GetCommand();
            }
        }
}

…. The code is sightly too long to post here (download the solution at the end), but the InsertCustomer looks like this:

private static void InsertCustomer()
{
    Console.WriteLine(“Enter new customer details”);
    Console.WriteLine(“Name:”);
    string name = Console.ReadLine();
    Console.WriteLine(“Email:”);
    string email = Console.ReadLine();
    CustomerManager.CreateCustomer(name, email);
    Console.WriteLine(“Saved”);
}

Summary

In summary, you can unit test against subsonic, but it involves hitting with the database. I hope you have found this of some use and provided you with a good insight.  In a later post I will discuss how we can mock SubSonic (Hopefully). 

SubSonic can be found at http://www.codeplex.com/actionpack

Download Solution: HowToUnitTestSubSonic.zip

Technorati Tags: , , ,

How To Unit Test – Interacting with the Database

One of the problems I have found with unit testing is there are a lot of people writing about the theory of TDD and Unit Testing and a lot of people writing about how to do simple Unit Testings (Assert.Equals(accountA.Balance, accountC.Balance).  But not so many people are talking about how to actually unit test real world objects and scenarios (or they are and i’m just completely missing them).  Over a series of blog posts, I hope to change that 🙂 All the posts will be tagged with MbUnit, TDD and Testing if you want to follow them easily.

In this first post, I will discuss unit testing with code which involves interacting with a database.  People have spoken about this before, but it is the most common scenario so I thought I would start here.  I have already wrote about mocking a database in Part 2 of my Rhino Mocks series on ASP Alliance. In this post, I will focus on unit testing and interacting directly with the database.

Why would you not want to interact with the database?

I thought I would start with saying why interacting with a database isn’t recommend during unit tests.  The main reason is because a unit should be a single, isolated block of code with no dependencies on other parts of the application.  Having a database breaks this as you need to ensure that the database is setup, populated and accessible at runtime (think from build servers).  By mocking out the database, we remove this dependency and our tests become more encapsulate and easier to maintain.

Another problem is that database tests are slow.  For every test you need to setup data, perform queries and tear down data, it all takes time and effort and distracts from the intent of the test. If you are running your tests every time you build, then this additional overhead definitely makes the process more painful.

Finally, if you use a central server to execute the tests against, you might have problems if other developers or testers are also testing the tests at the same time.

However, as I mentioned in the article, while mocking out the database is great when your testing your higher layers (API/Business Logic) you still have to test your actual data access against a database – otherwise how else will you know it works?  Most people refer to this as a integration test instead of a unit test as its breaking the machine boundary.

Scenario

For this sample, I will create a Customer Database where we can insert, update and delete Customer records.  Very simple application, but we have all had a similar requirement.  I’ll write the test code inline to demonstrate my thought process, if you want to know how I actually implemented the solution then the solution is downloadable at the end.

TDD/Unit Tests

The first thing we want to do is be able to do is connect to the database using a connection string from the configuration file.  For this, we need to obtain the connection string from the App.Config.

[Test]
public void GetConnStringFromAppConfig()
{
     DataAccess da = new DataAccess();
     string actualString = da.ConnectionString;
     string expectedString = System.Configuration.ConfigurationManager.ConnectionStrings[“DatabaseConnection”].ConnectionString;
     Assert.AreEqual(expectedString, actualString);
}

This is a test just to make sure that when we construct the DataAccess layer, the connection string property is populated correctly.  Once we have a passing test, we can move on.

The next thing we need to be able to do is be able to connect and disconnect from the database.

[Test]
public void ConnectAndDisconnectFromDatabase()
{
    DataAccess da = new DataAccess();

    bool connected = da.Connect();

    bool disconnected = da.Disconnect();

    Assert.IsTrue(connected);

    Assert.IsTrue(disconnected);
}

While this test does touch two different items, its important to disconnect from the database during the test so we might as well test both items. When executing this test, it fails due to a problem logging in to the database.  As all this does it try to connet to the database, we know its either because the server is down, your login credentials are wrong or the database doesn’t exist. As we know the server is up and we are using integration security it must be the database. For this solution I have created a database called HowToUnitTest and using Integrated Security as the login mode.

Next we need a way to actually execute tests on the server. For this I’m going to have a method which returns a SqlCommand which can execute a stored procedure.

[Test]
public void GetSqlCommand()
{
    string spoName = “spoTest”;
    DataAccess da = new DataAccess();
    SqlCommand response = da.GetSqlCommand(spoName);

    Assert.IsNotNull(response);
    Assert.AreEqual(spoName, response.CommandText);
}

This method will only be used internally and other methods will be accessible on the DataAcccess object to execute the SqlCommand.  That can be our next test.

[Test]
public void ExecuteSqlCommand()
{
    string spoName = “sp_who2”;
    DataAccess da = new DataAccess();
    SqlCommand response = da.GetSqlCommand(spoName);
    da.Connect();
    DataTable ds = da.ExecuteSqlCommand(response);

    da.Disconnect();
    Assert.IsTrue(ds.Rows.Count > 0);
}

Great!, we have a set of tests to execute a command on the database (even if all it does is return who is logged in). Now, lets actually do something involving data!  I want to be able to insert a customer into the database.

[Test]
public void InsertCustomerIntoDatabase()
{
    string spoName = “InsertCustomer”;
    DataAccess da = new DataAccess();
    da.Connect();
    SqlCommand response = da.GetSqlCommand(spoName);
    response.Parameters.Add(“Name”, SqlDbType.NVarChar).Value = “Customer Test 1”;
    response.Parameters.Add(“Email”, SqlDbType.NVarChar).Value = “[email protected]”;

    int rows = response.ExecuteNonQuery();
    Assert.AreEqual(1, rows);
}

This command will call InsertCustomer with Name and Email as a parameter.  However, this isn’t a great way to do it as it involves our calling code doing a lot of the leg work some i’m going to refactor the code to be a little bit easier to understand.  First, we need a customer object.

[TestFixture]
class CustomerTests
{
    [Test]
    public void CreateCustomer()
    {
        string name =  “Customer Test”;
        string email = “[email protected]”;
        Customer c = new Customer(name, email);
        Assert.AreEqual(name, c.Name);
        Assert.AreEqual(email, c.Email);
    }
}

Next, we want to call a method on the DataAccess to insert the customer which returns true if it inserted correctly.

[Test]
public void InsertCustomerIntoDatabase()
{
    string name = “Customer Test”;
    string email = “[email protected]”;

    DataAccess da = new DataAccess();
    bool inserted = da.InsertCustomer(new Customer(name, email));
    Assert.IsTrue(inserted);
}

This is great, we have changed our code to make more sense.  Now we have a customer object, we can do a lot more based on this.  For example, we could Delete a Customer or Update a Customer just by creating and calling the method on the data access object.  The problem with implementing those methods is that first we need some way to get a customer out of the database.  So let’s write a test for that.

[Test]
public void GetCustomer()
{
    string name = “Customer Test”;

    DataAccess da = new DataAccess();
    da.Connect();

    Customer c = da.GetCustomer(name);

    da.Disconnect();

    Assert.IsNotNull(c);
    Assert.AreEqual(name, c.Name);
    StringAssert.IsNonEmpty(c.Email);
}

Now we can get customers from the database, we can update customers.

[Test]
public void UpdateCustomer()
{
    string name = “Customer Test”;
    string updatedName = “Updated Customer”;
    DataAccess da = new DataAccess();
    da.Connect();

    Customer c = da.GetCustomer(name);

    c.Name = updatedName;
    da.UpdateCustomer(c);

    Customer c2 = da.GetCustomer(updatedName);

    da.Disconnect();

    Assert.AreEqual(updatedName, c2.Name);
    Assert.AreEqual(c.Email, c2.Email);
}

In this test, we are getting a customer, updating their name, requerying for the customer again and asserting that the details are correct.  This test looks fine, the first time we run the test it passes, however the next it cannot find the “Customer Test” customer and fails.  This is because that customer is no longer in our database, it is “Updated Customer”, we have created dependencies within our tests! Not great as we cannot easily isolated the tests from each other and tests cannot be run independently of each other.  What we really need to do is have a known state for the database before we start and clean up after ourselves once we finish.

MbUnit has a cool attribute called [Rollback] which wraps the test inside of a transaction and once it has finished, automatically rolls it back.  This solves our problem of needing to clean up after ourselves as MbUnit can do it for us. Just add the attribute under [Test] and the framework will do the rest for you.

The only problem then is having a known good state for the database.

Our UpdateCustomer test now looks like this:

[Test]
[RollBack]
public void UpdateCustomer()
{
    string name = “Customer Test”;
    string updatedName = “Updated Customer”;
    DataAccess da = new DataAccess();
    da.Connect();

    da.InsertCustomer(new Customer(name, “[email protected]”));

    Customer c = da.GetCustomer(name);

    c.Name = updatedName;
    da.UpdateCustomer(c);

    Customer c2 = da.GetCustomer(updatedName);

    da.Disconnect();

    Assert.AreEqual(updatedName, c2.Name);
    Assert.AreEqual(c.Email, c2.Email);
}

We insert a known customer in the database, and then try and return it.  InsertCustomer is being tested by another test so if that has a problem, the other test will fail as well to make debugging easier.  Next, we want to delete a customer.

[Test]
[RollBack]
public void DeleteCustomer()
{
    string name = “Customer Test”;
    string email = “[email protected]”;
    DataAccess da = new DataAccess();
    da.Connect();

    da.InsertCustomer(new Customer(name, email));

    Customer c = da.GetCustomer(name);

    da.DeleteCustomer(c);

    Customer c2 = da.GetCustomer(name);

    da.Disconnect();

    Assert.IsNull(c2);
}

You may notice that we have said that if a customer doesn’t exist, it should return null however, we haven’t got a test for this so lets write one.

[Test]
public void IfCustomerNotFoundReturnNull()
{
    da.Connect();
    Customer c = da.GetCustomer(“Unknown”);
    da.Disconnect();
    Assert.IsNull(c);
}

We don’t need a rollback attribute as we are not making any changes to the database. The only method left now is to return all the customers from the database.

[Test]
[RollBack]
public void GetAllCustomers()
{
    string name = “Customer Test”;
    string email = “[email protected]”;
    int insertCount = 5;

    DataAccess da = new DataAccess();
    da.Connect();

    for (int i = 0; i < insertCount; i++)
    {
        da.InsertCustomer(new Customer(name + i, email));

    }

    List customers = da.GetAllCustomers();

    da.Disconnect();

    Assert.IsNotNull(customers);
    Assert.IsTrue(customers.Count == insertCount);
}

One thing left to do is add a TestCategoryAttribute to all the tests to say they are Database related. By using TestCategory, we can target which tests you want to run.  If you know your integration server doesn’t have access to a database then you can setup the tests to run all but the Database category.  This way, the tests aren’t executed and the failing tests are because of a problem and not database errors.  This is also really useful if you know you quickly want to execute your tests and are sure the database code hasn’t changed. 

We now have a set of passing tests for our DataAccess object, however there is a lot of repeating code within our database tests which could be removed. By using the Setup and Teardown we can remove a lot of this duplicated code and make our tests more readable.

DataAccess da = null;

[SetUp]
public void TestSetup()
{
    da = new DataAccess();
}

[TearDown]
public void TearDown()
{
    da = null;
}

That is now requirement complete.  Some refactoring on the internals of the actual code should be performed but now we have a full suite of tests we can easily perform this would fear of breaking anything.

One thing I haven’t coded yet is validation. This should also be tested.

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void IfCustomerNameNullThrowArgumentException()
{
    string name = null;
    string email = “[email protected]”;

    Customer c = new Customer(name, email);

    Assert.Fail(“If we get here, an exception hasn’t been thrown”);
}

When it comes to writing high level tests, we have two choices. We can either mock out the DataAccess object as that has been fully tested and is what I would recommend, or we can simply write more tests like GetAllCustomers() which insert all the data into the database, do some processing, and assert the response cleaning up after itself. However, as mentioned at the beginning we are already stretching the definition of unit when testing and creating the Data Access so API/BI tests interacting with the database is definitely not advised.

Download Sample

Download the solution, including the tests and the implementation code.

http://blog.benhall.me.uk/Code/HowToUnitTest/HowToUnitTest_Databases1.zip

In summary, I hope you have found this post useful. I hope you can see what we are aiming from when writing tests and how we can go about keeping them small and isolated but still interacting with a database.  If we were using mocks, we would mock out the SqlCommand section which actually executed the tests against the database and replace this with our own internal code. See my article for more on this. 

Any feedback on this post would be most welcome, if you want me to write how to unit test anything, then please let me know.

Technorati Tags: , ,

assembly:InternalsVisibleToAttribute – Test Internal Methods

One of the problems with unit testing is that you can only test public accessible items within the assembly.  To solve this you have to make more items visible and increase your public interface in order to be able to unit test the item.  This is bad if you are planning on making your API useable outside of your own system as changing the public interface could cause breaking changes in other peoples’ systems.  It also means your internal system is exposed and useable to anyone.

The InternalsVisibleTo attribute solves this problem.  By including this within your AssemblyInfo.cs you can set which assemblies can access you internal items.  This means you can test your assembly without having to set everything to public.  You even get intellisense support with all of your internal methods listed.

If we have an assembly MyAPI which is tested by MyAPI.Tests.  If in our MyAPI.class we have the following code

public class MyPublicClass
{
    public bool IsValid()
    {
        return true;
    }

    private bool AllowedAction(string action)
    {
        if (string.IsNullOrEmpty(action))
            return false;

        return true;
    }
}

Which has the following test

[TestFixture]
public class MyPublicClassTests
{
    [Test]
    public void TestIsValid()
    {
        MyPublicClass cls = new MyPublicClass();
        Assert.IsTrue(cls.IsValid());
    }
}

In order for us to test the AllowedAction method we would have to make it public.  But then everyone using the API would be able to use it and we would be unable to refractor this method for the lifetime of the API.  By adding the attribute to the MyAPI.AssemblyInfo file and change the method from private to internal.

[assembly: InternalsVisibleTo(“MyAPI.Tests”)]

internal bool AllowedAction(string action)
{
    if (string.IsNullOrEmpty(action))
        return false;

    return true;
}

[Test]
public void TestActionAllowed()
{
    MyPublicClass cls = new MyPublicClass();
    Assert.IsTrue(cls.AllowedAction(“Test”));
}

This is great, we have only increased our public interface to our test method yet all the other assemblies cannot access it.  However, one problem with this is that if another malicious user came along with an assembly called MyAPI.Tests then they also would be able to access the internal actions.

Then is why it is recommend to sign the test assembly and include the public key within the attribute. In order to obtain the public key, you need to run the sn application on the assembly.

sn -Tp MyAPI.Tests.dll

The attribute would then look like this.

[assembly: InternalsVisibleTo(“MyAPI.Tests,PublicKey=00240000048…………de35d15ffae”)]

However, it means MyAPI must have a strong name/be signed.

An example of this being used is with the System.Data.Linq assembly, you can see that Microsoft use this attribute to allow the assembly to be tested by another assembly called DLinq.Unittests.

[assembly:InternalsVisibleTo(“DLinq.Unittests,PublicKey=########”)]

MSDN Link – http://msdn2.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

Technorati Tags: , ,