Unit Testing meets Umbraco 4.1
I was starting to fix a few bugs listed on CodePlex for Umbraco and a lot were related to adding foreign keys, primary keys, indexes, etc… in the database. I was quite happy to take up the much needed task of putting some real structure into the underlying table structure but once I started adding the constraints, SQL server started telling me there was problems. Turns out that adding the correct foreign keys and indexes started to expose quite a few bugs in the data layer. These bugs are mostly related to data integrity such as deleting rows from the umbracoNode table but not from the related cmsDataType table when removing an Umbraco Data Type. In light of knowing that there was a lot of testing ahead of me I decided to figure out how to get unit tests working with Umbraco (otherwise testing all of these data constraints was going to be painfully slow and tedious).
So what’s the big deal? Everyone writes unit tests for code! Well, if you’ve used the Umbraco API for anything, you’ll quickly realise that you can’t use any of the Umbraco APIs unless you’re using them in an ASP.Net web context and having a web context in a unit test framework is generally hard to come by without some tricky type mocking. Here’s where Microsoft’s Visual Studio unit testing framework comes in very handy! It’s actually very easy to make a Visual Studio unit test run in the same web context as your website and here’s how:
- Create a new Unit Test project in Visual Studio
- You’ll notice that it creates a .testrunconfig file in your Solution Items folder in your solution
- If you double click this file, it will present you with some options, click on the "Hosts" option:
- As seen above…
- Select ‘Run in default host’ (I’m actually not sure if this is required :)
- Choose ASP.NET in the Host Type drop down list
- Change the URL to test to be the URL that your web application is running under (or if you’re running in IIS, select Run tests in IIS)
- Choose the path to your web application files in the "Path to Web site" field
- Enter a "/" in the Web application root (assuming you’re not running in a virtual directory)
- Close and save the .testrunconfig file
- Now, in your unit test class, you just need to ensure that your tests are run with this config:
Now when you run your unit test, it will run in the context of your web application!
Caution!
A couple of things to consider with this configuration:
- If you’re automating unit tests using a build server, or similar, this isn’t going to work. This will work on your machine because it’s running under the Cassini context built in to Visual Studio. Build servers aren’t going to automagically be able to start up a Cassini server to run your tests under (well, i’m sure anything is possible, but i don’t think this is going to work out of the box). Running it under IIS could be an option however.
- Debugging doesn’t work quite the same as normal unit test. You can’t actually just set a break point and hope that it will stop code execution and allow you to debug but fortunately there’s a work around for this. You’ll have to add a user defined break statement in your code if you want to step through it:
System.Diagnostics.Debugger.Break();
- When you add this, an alert will pop up asking you if you want to debug so all you have to do is click yes and start debugging with another instance of Visual Studio. You can just keep this instance attached to the process and add breakpoints to this instance as much as you like.
Working in teams
As you can tell from the above configuration file, that this setup is specific to my computer which is why I’ve named the .testrunconfig file using my machine name: Shandemvaio. This is useful when working in teams (such as the Umbraco core team). Each member of the team can create their own .testrunconfig file and before running the tests just set the Active Test Run Configuration file to be their own… nice.
The Tests
So far I’ve written some tests for the Document object. I’ve started with this object since it’s probably the most important and is also the highest in the object hierarchy which means that the test written here will also be testing a lot of the base class functionality. We could write unit tests for months and still not have written tests for every circumstance so I’m merely focusing on unit tests that relate to data integrity. And here’s the ones written so far:
… and there’s lots more to come!