Homepage | About Me | Testing ASP.net Book | Best Blog Posts | Personal Projects | Follow me on Twitter | GitHub | SlideShare | RSS
Blog.BenHall.me.uk

How To Unit Test - RegEx and Technorati

Wednesday, October 31, 2007

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("<a href=\
http://technorati.com/tags/(.+?)\ rel=\"tag\">(.+?)</a>");
    MatchCollection collection =
        r.Matches("<a href=\"
http://technorati.com/tags/TestCode\" rel=\"tag\">Test Code</a>");

    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("<a href=\
http://technorati.com/tags/(.+?)\ rel=\"tag\">(.+?)</a>");
    MatchCollection collection =
        r.Matches("<a href=\"
http://technorati.com/tags/TestCode\" rel=\"tag\">Test Code</a>");

    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 = "<a href=\"
http://technorati.com/tags/TestCode\" rel=\"tag\">Test Code</a>";
    MatchCollection collection = Technorati.GetMatches(post);
    Assert.AreEqual(1, collection.Count, "Collection");
}

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

    List<string> 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<string> 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<Blog> 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<string> terms = new List<string>(3);
    terms.Add("MbUnit");
    terms.Add("TDD");
    terms.Add("Testing");

    List<Blog> 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: , , , ,

Labels: , ,

How To Unit Test - Using SubSonic and Rhino Mocks

Monday, October 29, 2007

"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<IFeedGetter>();

    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<IDataStore>();
    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", "User@TestMail.com");
    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: , , , ,

Labels: , , , ,

SubSonic and ASP.net MVC

Friday, October 26, 2007

So from my last few posts, you might know I'm a fan of SubSonic and Rob Conery (SubSonic creator) has accepted an offer to join Microsoft.  His role, working on the the MVC framework and SubSonic full time.

The ASP.net team seriously have some amazing people working for them and on this MVC framework, what an amazing team that would be to work in!!

"SubSonic will be the convention-driven toolset for Microsoft’s new MVC framework"  SubSonic will remain open source, but from the sounds of it, SubSonic will be the ORM for MVC. 

Jon Galloway always said "Microsoft should ship SubSonic..." I think they have just gone one better by employing the creator and leaving it open source!! How cool!

I can't wait for the first drop of the framework, going to take a lot of my attention I think.  It's going to rock!!

Technorati Tags: ,

Labels: ,

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 = "JohnSmith@GoogleMail.com";
    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<Customer> 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 = "Customer@Test.com";
    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<Customer> customers = CustomerManager.GetAllCustomers();
    Assert.AreEqual(3, customers.Count);
}

With the implementation looking like this:

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

    List<Customer> custs = new List<Customer>();

    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:\Users\Ben Hall\Documents\Visual Studio 2008\Projects\HowToUnitTest\SubSonic.Tests\CustomerManagerTests.cs:line 69    E:\Users\Ben Hall\Documents\Visual Studio 2008\Projects\HowToUnitTest\SubSonic.Tests\CustomerManagerTests.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 = "JohnSmith@Gmail.com";

    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: , , ,

Labels: , ,

The One with Red Gate...Sorry The One With 3 Red Lights ...

At SQLBits, Dave grabbed us (Andras, David and myself) at the end for a quick podcast interview and they have just uploaded the interview in their SQLBits podcast.  Have a visit...

http://www.nxtgenug.net/Podcasts.aspx?PodcastID=42

http://www.red-gate.com/

Technorati Tags: ,

Labels: ,

DDD6 Agenda

Wednesday, October 24, 2007

DDD6 agenda has now been posted online as well.  I've got one of the last session blocks of the day up against Swaggily Fortunes - great!  But it looks to be a good mix across the 4 rooms to keep everyone happy.

http://www.developerday.co.uk/ddd/agendaddd6lineup.asp

Technorati Tags: ,

Labels: ,

DDD6 Registration is now open!

DDD6 Registration is now open!

You can now register for DDD6 on Saturday 24th November 2007: http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032357828&Culture=en-GB. The agenda will be posted on the DDD6 site shortly.

Don't miss it. It gets booked up REAL quick so register now!

Technorati Tags: ,

Labels: ,

Setting up SubSonic

Monday, October 22, 2007

SubSonic is a great framework!  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.

In this post, i'm just going to quickly discuss how you can get SubSonic to generate the classes for your database.  The first thing I always do is add a reference to "C:\Program Files\SubSonic\SubSonic 2.0.3\SubCommander\sonic.exe" in my External Tools dialog.  This way, whenever I need to refresh my database schema, I can select the menu item and it will go off and create the classes for me.

image

A subsonic item will then be listed in your Tools menu.

image

The next task is to configure your App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="SubSonicService" type="SubSonic.SubSonicSection, SubSonic" requirePermission="false"/>
  </configSections>
  <connectionStrings>
    <add name="DatabaseConnection" connectionString="data source=localhost; initial catalog=ACustomerDatabase;Integrated Security=True;Application Name=SubSonicBlogPost;"/>
  </connectionStrings>
  <SubSonicService defaultProvider="CustomerDatabase">
    <providers>
      <clear/>
      <add name="CustomerDatabase" type="SubSonic.SqlDataProvider, SubSonic" connectionStringName="DatabaseConnection" generatedNamespace="CustomerDatabase"/>
    </providers>
  </SubSonicService>
</configuration>

There is three main sections, first the configSections which must appear first (directly under configuration).  Next, we have the connection strings for the databases. Finally, we just setup the SubSonic service, giving it a name (CustomerDatabase) and the connection string to use.  There are more options we can add here, but to keep things simple I will just give it the namespace I want my generated code to live in.

Next, add a reference to the SubSonic.dll assembly.  This can be found at "C:\Program Files\SubSonic\SubSonic 2.0.3\SubSonic.dll".  You also need to add a reference to System.Web and System.Configuration.

Finally, your assembly needs to be strongly named as SubSonic.dll is now also strongly named.  You can do this in the signing tab in the project properties in Visual Studio.  This is a fairly new thing, however does solve problems with the GAC.

That's it.  Select the external tools menu item and SubSonic will go off and do its thing. You will then have a folder called Generated in your project directory. You need to click right and include the folder for it to be compiled with your application.

image

You can now write the code below, upon calling Save, the customer is saved into the related table!

Customer c = new Customer();
c.Name = "Test";
c.Email = "Customer@Test.Com";
c.Save();

I do love SubSonic, I think it's great.  The fact you can get up and running so quickly is great.

Technorati Tags:

Labels:

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 = "Customer@Test.com";

    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 = "Customer@Test.com";
        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 = "Customer@Test.com";

    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, "Customer@Test.com"));

    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 = "Customer@Test.com";
    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 = "Customer@Test.com";
    int insertCount = 5;

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

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

    }

    List<Customer> 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 = "Customer@Test.com";

    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: , ,

Labels: , ,

Beginning to Mock with Rhino Mocks and MbUnit - Part 2

Monday, October 15, 2007

The second part of my Rhino Mocks and MbUnit articles was posted today.  In this article I cover how to mock a DAL and a Web Service. Any feedback (good or bad) welcome!

Read the article at:
http://aspalliance.com/1456

Not sure when the third and final part will be released, but hopefully will be before the end of the year.

Technorati Tags: , , ,

Labels: ,

BT Home Hub - Built in NAS

Saturday, October 13, 2007

At home I have signed up for BT Total Boardband, which touch wood has been working well so far.  With Option 3, you get the BT Home Hub and Phone which is a wireless router with a few ports at the back so you can plug in your phone for VoIP, two ethernet ports (using one for my powerline adapter) however tonight, I went into the Network on my laptop and noticed a item called Thomson.  Immediately I thought we had been hacked!! It wasn't showing up in the admin section so started to get a little bit concerned. In one of the pages it had this unknown device listed as being connected to ethport3 (yet only two ethernet ports) with an IP of 192.168.1.253.  A little bit of digging and I found that this is for the usb port on the hub.

So, I plugged in a small USB pen (which I got free from Google), went back into Network and there was a shared folder of bt_7g,  this was my usb pen.  My home hub is now a NAS box with full read/write ability.  Very cool and free!  On bad point is that my 4Gb pen I'm not using doesn't fit,  need a little cable so it can break out the back.

Technorati tags: ,

Labels:

More than just a number

Friday, October 12, 2007

Office Today was Red Gate's 8th birthday - happy birthday Red Gate!!  To celebrate some of the staff (Office Admin and HR) got together and organised a surprise for everyone!

Firstly they had made the office more colourful by putting up some balloons and a happy birthday banner, but at lunch they also laid on some champagne and cake!  We have champagne for product releases, but not cake :)

Not only that, but they had got everyone in the company a personalised t-shirt with their employee number and start date!! How cool!!

Front

Back

If you want to join in the fun, we are actively recruiting at the moment.  Take a look at the careers page on the website. 

Technorati Tags:

Labels:

DDD Scotland and DDD6 Voting

dddscotlandTwo bits of DDD news today.  Firstly, a DDD in Scotland has been requested for a long time and today Colin announced some details.

It will be in May 2008 at Glasgow Caledonian University.

More information on his blog.

The second bit of news relates to DDD6 and voting has now opened. Cast your votes for which sessions you want to see at http://www.developerday.co.uk/ddd/votesessions.asp.

I've submitted a talk on MbUnit, with a look at MbUnit Gallio.  Your votes are most welcome!  The abstract is:

Testing Your Applications With MbUnit Gallio

Everyone seems to be talking about TDD, Unit Testing and Integration Testing, but how do you get started using these techniques on a real project? How do they affect the way we design and develop software?  In this talk, I will cover how to begin unit testing and factors which should be taken into account when designing and developing applications. I will then demonstrate how the new MbUnit Gallio framework can revolutionise the way you write your tests.

Other interesting sessions I spotted are:

Many hands make life work (Gary Short)

N-tier applications with Linq (Ian Cooper)

Why IronRuby (Dave Verwer)

Why do I need an Inversion of Control Container (Mike Hadlow)

All aboard the MonoRail (Ian Cooper)

Oh and mine ;).  There are sessions to meet everyone's taste. Get voting!!

Technorati Tags: ,

Labels: ,

SQL Connection - Application Name

Thursday, October 11, 2007

While at SQLBits, both Dave and Simon mentioned passing the Application name as part of the connection string to make debugging easier.  This was the first time I had heard this, so I decided to have a little look so I fired up my trusty linq to SQL Console and SQL Profiler and executed some queries.

Without using the application name parameter, SQL Profiler's results looks like this.

image

From this, SSMS uses the parameter and you get a nice friendly name however my console application doesn't and you get the information about the SqlClient data provider being used.  Helpful, if you only have one SqlClient running, unlikely if you have a production server.

If I modify my connection string and include the Application Name option as part of the connection string it would look like something below.

<add name="LinqConsole.Properties.Settings.NorthwindConnectionString"
    connectionString="Data Source=.;Initial Catalog=Northwind;Integrated Security=True;Application Name=LinqConsole"
    providerName="System.Data.SqlClient" />

Now if we execute the application, Sql Profiler uses the name in the connection string within the Application Name column. Much better and more useful!

image

But it's not only in Sql Profiler where its used, if you execute sp_who2 to see which connections are made to the server you can see that the program name is include.  Very useful for detecting resource leaks...

image

If that wasn't enough of a reason, then you can filter down which items are actually traced based on the application name.  When creating a new trace, simply go to Events Selection tab, select Column Filters and enter your application name in the Like (or Not Like) box.

image

I'm sure its used in many more places, however this was just a quick look at what it actually is and from now on I will definitely be including it in all my connection strings - it's just good manners for anyone using or support your application.

Technorati Tags:

Labels:

NETGEAR HDX101 / HDXB101 Powerline Ethernet Adapter

XE202_3-4Lft 001

Today I received my order for the Netgear NDXB101 Powerline Ethernet adapter so I thought I would write about my initial thoughts. The HDXB101 contains two HDX101 adapters which plug into the wall and allow you to connect a standard ethernet cable to them, once you connect the second device to a second plug socket in your house you have a network!

There are a number of reasons why I purchased this over Ethernet.

Firstly, speed was a concern.  I am using this to connect my desktop to my router at opposite ends of the house and there are a number of other wireless networks in the area and interface was affecting the speed.  Plus, streaming video or anything over wireless isn't great.

Secondly, while security isn't a huge problem if you use WPA (compared to WEP) it still is a concern.  All the traffic sent between the two devices is encrypted.

I need to connect my XBox 360 to my router so how, which meant buying an overly priced wireless adapter.  Now, I can just buy a single one (buying the pair works out a lot cheaper) and use this to connect to my existing adapter which is connected directly to my router and have the speed advantage.

Plus, from the viewpoint of Vista, it's connect to directly to my router.  No messing around waiting for it to detect the wireless access point or access keys.

While the adapters don't need any software installed to work correctly (just plug in and go), Netgear provide some software to help manage your network. You can also use this software to rename the network ID, just incase of any other network is connected to the same power supply (like in apartments).  The software also provides an indication of the speed which you are likely to get out of the connection.  While it isn't 200Mbps (and I never expected to get that), it is better than wireless.  No problems with the software, or the devices, working in Vista.

netgear

However, I did have two problems when I was setting up the kit. They don't work correctly (slow speed) if you connect them via a surge protector, so I need to go buy something from Tesco's on Saturday as running out of normal plugs now.

Secondly, I needed to plug the Ethernet cable into Ethernet port 2 on my BT Home hub, Ethernet port 1 didn't work.  Apart from that, they seem to be doing the job. 

Technorati Tags: ,

Labels:

C# Null Coalescing Operator (??) and String.Empty

One of the features of C# 2.0 is the null coalescing operator (??) which says that if the value is not null, then use it otherwise use another value.

For example, if we wanted to use the myCust variable if it wasn't null, or if it was use order.Cust then we would write.

Customer c = myCust ?? order.Cust;

Using 1.1, we would use the ternary operator (?) and it would have been

Customer c = if(myCust != null) ? myCust : order.Cust

We could have chained this together, so we could also check order.Cust to see if that was null and provide an alternative to that.

However, one problem I had was with String.Empty.  I wanted to test to see if a string was returned from a method and if it wasn't, use the system default.

String importantStr = myObject.GetImportantString() ?? sysDefault;

The problem is that myObject.GetImportantString() returns string.empty if it cannot be found. The operator doesn't treat string.empty as a null and uses it as the variable value. Not what I wanted at all! 

Sadly, I had to go back and use the ternary operator.

String importantStr = if(!string.IsNullOrEmpty(myObject.GetImportantString()) ? myObject.GetImportantString() : sysDefault;

Sadly, myObject.GetImportantString() is an expensive call so calling it twice is bad. So I had to resort to the old way of doing things

string temp = myObject.GetImportantString();
string importantStr = sysDefault;
if(!string.IsNullOrEmpty(importantStr))
   importantStr = temp;

Oh well, still a useful operator.  Just not for strings...

Technorati Tags:

Labels:

Balloon Tips, NotifyIcon and Windows 2000

While i'm sure this isn't relevant to most of my readers, again this has bugged me on and off for the last hour so thought I would blog for future reference for anyone else.

I've got a nice working SysTray application which displays a balloon tip message, that when a user clicks the tip it launches another separate dialog.  This worked great on Vista and XP however I decided just to do a little test on Windows 2000 (yes, its still used!).  I found that when you click the balloon tip on 2000, nothing happens.  Turns out this is because the shell_NotifyIcon is a different version on 2000 than XP/Vista and the event isn't being handled correctly.

The only way I found to fix this is no to only have the balloon tip click (BalloonTipClicked) event wired up but also the Click event on the actual NotifyIcon class wired up as well.  This way, when the user clicks the icon is will display.  Sadly, if they click the Balloon tip nothing will happen but at least there is still some support for 2000 in place.

I then found that on Vista/XP where it worked correctly before, now when the systray icon is clicked both the BalloonTipClicked and the NotifyIcon.Click event are fired, hence opening the form twice!  Quick solution was to use a boolean member variable which knows if the form is currently visible or not.  Nothing fancy...

Technorati Tags:

Labels:

NotifyIcon icon still visible after closing form

Wednesday, October 10, 2007

I''ve been playing around with the NotifyIcon class within C# recently but been having one major issue with it.  The class itself allows you to place an icon in the systray (down by the clock) and for it to display the standard balloon tip dialog.

However, the problem I was having is that when my calling parent closed my NotifyIcon would still be in my SysTray.  The only time it was removed was when I hovered over the icon with my mouse, then it would automatically disappear. Not great from the users point of view!

The MSDN page says nothing about this however I did find a useful forum post on the subject.  The solution is to manually call dispose and more importantly, set the variable to null.

The code is shown below, the notifyIcon variable is a member variable (private NotifyIcon m_notify;)

protected override void Dispose(bool disposing)
{
    if(m_notify != null)
    {
        m_notify.Visible = false;
        m_notify.Dispose();
        m_notify = null;
    }

    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

After inserting this code, when my parent form is closed the notify icon also disappears.  This took me a while to find out about, hopefully I have saved you some time.

Technorati Tags:

Labels:

SQLBits - Post Conference

Monday, October 08, 2007

On Saturday I attended the first SQLBits conference.  The one day event was organized by UK SQL Server MVPs and focused on different areas of SQL Server with the sessions being categorised as DBA, Dev, BI and 2008.  The sessions themselves where very good, with Dave's Top 10 keywords and Simon's SQL Myths sessions being my favourites but there was a very good mix of different sessions to satisfy the different roles of attendees.  From what I got, most people seemed to be DBA's which was pleasing.  Dave was doing his podcasting rounds so keep an eye ear out for that...

One bonus which I wasn't expecting was that if you filled out the feedback form, you will be sent a £30 book voucher!! Take note DDD6 organisers ;) Very cool.  Think I will get a SQL Server book...

Afterwards, Red Gate paid for coaches to take people back (we also paid for the coaches to get people to TVP from the station in the morning as well) to the station but also onto the Group By event.  This was cool was at previous events this has always been a real pain.  The evening event was held at Reading Bowl, I had my reservations about bowling but it was actually really good fun.  The turnout wasn't great, but still had a really good time.

I'm now really looking forward to 2008 CTP 5 being released (was told its due this month sometime).  I have post about CTP 3 before, use the search box at the side to find them as sadly I didn't label them (sorry...).

On a side-note, I had my confirmation for TechEd Europe today!!! My first time @ TechEd and really looking forward to it.

Technorati tags:

Labels: ,

Visual Studio Solution Uploader - Live Writer Plugin

Saturday, October 06, 2007

Over the past few days I have been creating a Visual Studio Solution Uploader as a Live Writer plugin. The plugin allows you to select a Visual Studio 2005 solution file, when selected the solution and the directory will be zipped and included in the blog post. All done in a few simple steps. If you wish, there is also the option to build the solution and have them also uploaded as a separate zip file.

This is an early release as I want to get some feedback about what you think, there is more than likely to be one or two bugs. I have found it really useful, no longer do I have to manually zip and upload samples along with the blog, I can just get my plugin to do it all for me!

Download Installer

The plugin uses the same settings as Windows Live Writer for publishing images, so if your blog provider doesn't support uploads you will need to have your FTP configured.

Big thanks to Scott who helped me get my settings correct.

Screenshots

image

image

image

Any questions or comments, please let me know.

(Update: I blogged this on the 3rd but I uploaded it to the wrong blog!.... Sorry).

Technorati tags: , ,

Labels:

Sandcastle September CTP and DocProject 1.8.0 Release Candidate

Thursday, October 04, 2007

The Sandcastle team released the September CTP on Monday, more information here.

Dave Sexton has also released DocProject 1.8.0 RC.  He has put the main new features on his blog post - DocProject 1.8.0 Release Candidate.

There is also a Preview post which he did a few weeks back.  I'll look into this more tonight. 

Download it from CodePlex

Technorati Tags:

Labels:

Windows Live Writer Plugin - Installation

Wednesday, October 03, 2007

After you have created your amazing live writer plugin, you will need to actually install the plugin on the users machine.

In this post, I will discuss how to create an installer (msi package) for your plugin which you will be able to provide to your users.

I will be using the Setup Project template within Visual Studio 2005.  I would have used WiX but I think it's a lot more complex to setup initially.  One day, I might look at WiX in more detail.

image

After you select the project, you will be shown a File System window with the installer config files in your solution. The installation allows you to place files in the Applications folder, the user's desktop and the user's program menu.  If you click right the solution project, you can view different parts of the installer.  For example, you can select Registry to make changes to the registry during the installation.

image

In the above dialog, if you add a file to the Application Folder, then when the installer is executed, the file will be placed there.

When creating the setup, the first thing to do is set where you want the installer to put all the files. In this case, it will be the plugin directory for Windows Live Writer.  Note, in Beta 3 this has moved.

Within the properties for the Application Folder, set the DefaultLocation to

[ProgramFilesFolder]Windows Live\Writer\Plugins

image 

This simply says, place all the files in the user's program files\Windows Live\Writer\Plugins\.

The next thing to do is to add the files. Right click in the blank Application Folder and select Add > Project Output.  This takes the output from the other projects in the solution and includes it in the setup.

image

However, there are a number of assemblies listed which we don't want to install (they are referred by the API, but don't require deployment).  Highlight all the files you don't want, go to the properties dialog and set the Exclude property to True. 

image

The files will then be removed from the Application Folder.  If you look in Solution Explorer, you can see that it has listed all the Dependences, however has excluded the selected ones.

image

You can now build the project and the output text should be similar to below.

------ Starting pre-build validation for project 'LiveWriterInstaller' ------
------ Pre-build validation for project 'LiveWriterInstaller' completed ------
------ Build started: Project: LiveWriterInstaller, Configuration: Debug ------
Building file 'F:\Users\Ben Hall\Documents\Visual Studio 2005\Projects\LiveWriterInstaller\Debug\LiveWriterInstaller.msi'...
Packaging file 'writer.png'...
Packaging file 'WindowsLiveWriterInstallation.dll'...
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

From the output, you can see that it has created us a msi file.  This is what we need to give to our users in order for them to be able to install the plugin.

When we launch the MSI, it will look just like a normal installer.

image

As you can see from the screenshot below, the location for where the installer will copy the files too is set correctly by default.

image

As for repair and uninstall part of the installer, they come for free by default.

You can now upload the MSI to your blog / Live Gallery for everyone to enjoy.  This has just been a quick overview, but it should be enough to get you started.

Technorati tags:

Labels: