Testing the NuSoft Framework
posted on 07/09/08 at 12:45:25 am by Joel Ross
I've started up a new project that is using the NuSoft Framework, and because of some of the interactions, I realized it would be much, much easier if I had a set of automated tests, rather than going through the process of firing up an application, going through a WCF service, and then eventually hitting the code I needed to test.
I realize this isn't earth shattering for those used to doing TDD or unit testing, but it's not something that I've done a lot of in the past. On one project, we had well north of 100 tests, but they were all isolated to a particular hairy part of the code and it was rather self-contained.
To be honest, the NuSoft Framework is not ideal for unit testing, since the data persistence is pretty well embedded into the entities. That's OK in my mind, since I've rarely seen a case where my persistence layer has changed. Once, I knew I had to build for different databases, but that was known up front, and not added after the fact. That may be viewed as a naive attitude, but it's worked for me so far.
Anyway, there's a reason I didn't call this "unit testing" but rather referred to it as just testing. It's not unit testing, since it's not testing just one thing. It's more like an integration test, since it's also going to rely on a known state of the database.
Essentially, what you have to do is manage your own transaction, and then at the end, after you assert that your desired action worked, you roll it back, leaving the database untouched. If you want to isolate your tests in their own assembly, you're going to need to add an attribute to your business layer so you can gain access to some of the internals of the business layer:
1: [assembly: InternalsVisibleTo("Northwind.Tests")]
This gives us the ability to call one of the internal overloads on Save() where you can pass in a helper. This ensures the Save will participate in the existing transaction, and not create its own (which it does when you call Save() with no parameters). Here's an example that would test being able to insert a customer:
1: [Test]
2: public void CanInsertCustomer()
3: {
4: Customer customer = Customer.CreateCustomer();
5: customer.CustomerId = "jross";
6: customer.FirstName = "Joel";
7: customer.LastName = "Ross";
8:
9: using (SqlHelper helper = new SqlHelper())
10: {
11: try
12: {
13: helper.BeginTransaction();
14: customer.Save(helper);
15: Assert.That(customer.IsNew == false, "customer.IsNew was true but should have been false.");
16: }
17: finally
18: {
19: helper.Rollback();
20: }
21: }
22: }
The difference between this code and the code you would normally use is that you wouldn't manage your own helper and you'd call customer.Save(), not customer.Save(helper). If you dig into the guts of Save() with no parameter, you'll see it does essentially what I'm doing here. It creates a SqlHelper and opens the transaction. The difference is that if the insert works, the transaction is committed, and here the transaction is always rolled back, ensuring that the database isn't affected and leaving it in a known state for other tests.
I'm still new to automated testing, but it definitely does make being able to change existing code much easier and give me more confidence when I am doing that. And seeing all of the green lights in NUnit feels pretty good too.
Oh - the NuSoft Framework has a logo now (the one at the top of this post). What do you think of it?
Categories: Development, C#, RCM Technologies
5 comments
I would certainly recommend a host of unit tests for the validation of the entities' business logic. For instance, testing if an Order has to have at least one order line in order to be saved. This can be accomplished without attempting to do a .Save(), and instead concentrating on the new .GetValidationErrors() method.
Just my opinion... gon't hate me for it 8^).
The example was probably a bad one in it's simplicity. But, those are the types of tests that we could generate as part of the framework if we wanted to.
I do, however, disagree that we shouldn't be testing the actual persistence. On just about every project I've used the NuSoft Framework on, I've had a need to either override or handle one of the "On" events that fires as part of the Save() - since we don't have a way to test them independently, this is the best I can come up with - for now.
I actually did - within an hour - modify the code so that I could mock the data layer completely, meaning I could test just what I wanted to test and not worry about the database. More on that later.
Joel
That seems like the answer I was waiting for (question from Codeplex http://www.codeplex.com/Thread/View.aspx?ProjectName=kineticframework&ThreadId=45360). Can you use a mocking framework like RhinoMocks now?
It would be great if you could release the new version for testing.
Cheers!
/Kalle
The changes I made to allow you to mock out the IDataReader are in the trunk. There's a seam there to have the framework get an instance of IDataReader from your own factory, and you can just mock that out with RhinoMocks.
Mocking IDataReader is not fun, and takes a while to get the interactions correct, but it can be done.
