A Journey With NHibernate (and DDD) - Part 3
Wednesday, July 19 2006 3 Comments
I decided to somewhat rename this series to include Domain-Driven Design. It became apparent after Part One and Part Two that I will spend a lot of time just developing the domain model. I could either skip this step and start with a fully functional model, ready for persistence, or I can slow down a bit and focus on creating the domain model as well. Due to some recent feedback I am choosing to focus a bit on modeling the application. This will of course delay the NHibernate specifics, but a good understanding of the object model is crucial to effective object-relational mapping. On we go….
Before we get started with the tests (as I promised), I’d like to mention that I received some good advice from Howard Dierking about how I was approaching the development. He suggested that I refrain from creating an entire solution structure up front, and delay such details until a need arises for them. I hadn’t actually created these projects in my pet project (I was more thinking-out-loud), but it is worth mentioning that the project structure in part 1 is something that, from experience, I already had in mind what I wanted. Focus on the task-at-hand, which is the Domain Model. Speaking of, let’s start with our test list…..
When testing, whether you practice test-driven development (TDD) or not, it is important that your tests and your code follow each other closely. Venkat Subramaniam compares this to walking on a beach, one foot following the other. I’m not a TDD purist, but I try to write the tests first to let the consumer (the calling code) drive the design.
I wrote down (yes, on paper) a simple list of test that I’d like to implement. In no particular order, here they are:
- Can create an Employee
- Can create a Customer
- Can create a Video
- Can create a VideoGame
- Can create a Console
- Can create a Movie
- Can create a Game
- Can query for an Employee by EmployeeId
- Can query for a Customer by CustomerId
- Can create an Account
- Can associate a Customer to Account
- Can create a Transaction for an Account
- Can create a Rental for an Item from a Transaction
Tests in bold are the ones I’ll implement first. With a list of tests like this, it’s really up to you what gets implemented next. Focus on a small set and implement them, all the while keeping an entire list of tests you are working on, test that will come later, and tests that have been completed. This method of working is described in detail in Test-Driven Development by Kent Beck.
Here’s what my solution looks like:
I’ve only implemented the bare minimum on these classes, just enough to show the diagrams from last time. In order to test this class library, let’s add another class library project and call it Flux88.Videocracy.DomainModel.Tests.
We need to add a couple of references here. First, we are going to be testing objects from the Domain Model project, so we need to add a project reference to that. We will also be utilizing NUnit for our tests so we need to add a reference to NUnit.Framework.dll.
Here’s a skeleton class for Employee Tests:
using System;
using Flux88.Videocracy.DomainModel;
using NUnit.Framework;
namespace Flux88.Videocracy.DomainModel.Tests
{
[TestFixture]
public class EmployeeTests
{
//called before every test
[SetUp]
public void SetUp()
{
}
//called after every test
[TearDown]
public void TearDown()
{
}
}
}
You’ll notice I have a using statement for both my domain model and NUnit, as I will use these namespaces frequently in my tests.
The attributes you see above the class & methods are NUnit attributes. [TestFixture] denotes a class that has tests. When the test runner loads my test dll, it will notice this and parse the class for test methods (we’ll see in a second). The [SetUp] method is called exactly once before every single test. This is where you’d create any objects or set any preconditions for every test. The [TearDown] cleans up any objects or junk left over from the test. Each test should be able to run independently of any other. Don’t count on one test running successfully before another test can run.
Ok, our first test on the list was CanCreateEmployee. In this test I want to make sure that I can instantiate the Employee class and take in any invariants in the constructor. We don’t ever want to have an employee without a name, so name will be a constructor parameter to enforce that all new Employees have a name. Also all employees will be either a Customer Service Rep or a Manager, so we’ll take that in as well.
[Test]
public void CanCreateEmployee()
{
Employee emp = new Employee("Joe", "Blow", EmployeeType.CustomerServiceRep);
Assert.AreEqual("Joe", emp.FirstName, "FirstName wasn't set properly.");
Assert.AreEqual("Blow", emp.LastName, "LastName wasn't set properly.");
Assert.AreEqual(EmployeeType.CustomerServiceRep, emp.Type, "Employee type was not set.");
Assert.AreEqual(0.0, emp.HourlyWage, "Wage was not initially 0.0");
}
Decorating the method with the [Test] attribute tell NUnit to run this method as a test. I create an employee and pass in the name & type, then I run a couple of NUnit’s Asserts to verify that the properties were set properly. I decided that HourlyWage was not a required input. Right now there is no real need to do anything with HourlyWage, so I’m not going to test this. You’ll notice that the Assert.AreEqual verifies that the two values are equal, and if not it fails the current test with the specified error message. If it makes it to the end of the method without any errors, your test passes!
Ok, now it’s time to compile. Oops! I get an error. I haven’t yet implemented the constructor that takes in the parameters. I’ll add that and compile again. Also I’m going to make the default constructor private because I don’t want any callers to be able to instantiate the employee without filling out those parameters. Compiling now succeeds and we’re ready to run the test.
Load up the NUnit GUI and open your new test dll. You’ll see that NUnit organizes your tests hierarchically. We only have 1 test, called CanCreateEmployee. Lets run it now.
The tests pass! Now it’s time to refactor. Right now I’m directly setting the first name and last name in the employee class, but employee doesn’t actually own these properties, Person does. A simple refactoring is in order. I’ll add the invariants to the Person class and keep the only Employee specific invariant to the employee class. This is what it looks like now:
public Person(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public Employee(string firstName, string lastName, EmployeeType type)
: base(firstName, lastName)
{
Type = type;
}
You can see that the employee class is now passing arguments to the parent class’s constructor. We run the test again to make sure that the property setting still works, and it does. This kind of simple refactoring is something you regularly do as you develop. The tests are there to back you up and assure you that you haven’t broken anything.
I started off kind of slow to illustrate how the process goes. I’ll speed up as I implement the remaining tests.
Can Create Customer was the next test, but it’s painfully similar to the above example, so I will just implement it and move on. (…. a bit of typing ….) ok, now that test is passing. Let’s move on to Can Create Video.
Let’s give some thought on what a video is. A video is a representation of the actual item you hold in your hands at the video store. It contains a movie, but there is a distinction here. I chose to separate the movie details from the video following the Type-Object pattern (as it turns out, their example is about movies too! what luck!). Since a video cannot exist without a movie, we should probably test the movie creation first. Here’s CanCreateMovie:
public class MovieTests
{
[Test]
public void CanCreateMovie()
{
Movie superTroopers = new Movie("Super Troopers", MovieCategory.Comedy, 2001);
Assert.AreEqual("Super Troopers", superTroopers.Name, "Name wasn't set");
Assert.AreEqual(MovieCategory.Comedy, superTroopers.Category, "Category wasn't set");
Assert.AreEqual(2001, superTroopers.Year, "Year wasn't set");
}
}
I make sure and pass in a title, category, and year. If the categories are to be user definable later on, we’ll probably opt for a class instead of an enum, but again we defer this decision until it is actually needed.
I run the tests again, and:
…all passing.
I’m ready to write our Video test, but here’s where things get tricky. We identified previously that a video cannot exist without a movie, but one of the testaments to good unit testing is writing tests that do not depend on other tests. The purist solution to this is to extract an interface for Movie (called IMovie) that can be faked out in the test (A fake is a class implements the interface with known/precalculated values that you can test upon). I think this approach is a bit overkill for a simple class like Movie, but if the class connected to another resource, such as a database, web service, or file, then I’d definitely stub out the interface for testing.
public void CanCreateVideo()
{
Movie caddyShack = new Movie("Caddy Shack", MovieCategory.Comedy, 1980);
Video v = new Video(caddyShack, VideoFormat.DVD);
Assert.AreEqual(v.Movie, caddyShack, "Movie wasn't set.");
Assert.AreEqual(VideoFormat.DVD, v.Format, "Format wasn't set.");
}
This test actually depends on two things. One, mentioned above, is CanCreateMovie. The other is a valid implementation of Movie.ToEquals(), since we are equating the two movie objects. This can make your tests somewhat ambiguous, as the tests will fail for different reasons. I have yet to find a really good balance between Interfacing everything and having tests that depend on other tests. For something this trivial I won’t worry about it, but for more sophisticated tests I will be sure and isolate the test.
Right now this is what NUnit looks like:
All of the tests are passing and we’re at a good stopping point.
Next time we’ll see some more complicated tests. Until then….
Shey's Rebellion
7.20.2006
3:26 PM
I can't wait to see more Unit Testing in action