I'm preparing a chunk of code for open source release in January. Among many other things it has a persistence layer wrapper with a "provider" for NHibernate, yet another validation framework, and a few goodies for building smart clients. It's not dissimilar to Ayende's Rhino Commons in fit or purpose. Some parts would suit any application. Others are targeted at the "smart client" domain. For example I have a front controller for windows forms. Wacky.
This is the first of a series of code-focused posts where I'll attempt to explain the motivations behind the major elements of the codebase and/or explain some steps in getting the codebase ready for public consumption.
----
I'm plowing through a substantial chunk of the test code having decided to convert test fixtures over to the specification style Scott and JP have been advocating. Essentially the technique involves using multiple classes to describe the contexts (BDD-style) for testing a particular behavioral unit. Most often, these tend to be individual classes.
Here's an example of part of a specification for a infrastructure class, TransactionElection, that supports our unit of work implementation (more on that later):
using System;
using NUnit.Framework;
using Rhino.Mocks;
using XEVA.Framework.Model.UnitOfWorkImpl;
using XEVA.Framework.Specs;
using XEVA.Framework.Model;
namespace Specs_for_TransactionElection
{
[TestFixture]
public class When_an_election_is_started : Spec
{
private ITransaction _stubTransaction;
private TransactionElection _election;
protected override void Before_each_spec()
{
_stubTransaction = Stub<ITransaction>();
_election = new TransactionElection(_stubTransaction);
}
[Test]
public void The_result_should_be_in_progress()
{
Assert.AreEqual(_election.Result, TransactionElectionResult.InProgress);
}
}
[TestFixture]
public class When_votes_are_cast : Spec
{
private TransactionElection _election;
private ITransaction _transaction;
protected override void Before_each_spec()
{
_transaction = Mock<ITransaction>();
_election = new TransactionElection(_transaction);
}
[Test]
public void A_single_no_vote_should_rollback_the_transaction()
{
using (Record)
{
_transaction.Rollback();
}
using (Playback)
{
_election.CreateVote();
_election.VoteNo();
}
}
[Test]
public void A_no_vote_will_leave_the_election_with_a_finished_and_voted_no_status()
{
using (Record)
{
_transaction.Rollback();
}
using (Playback)
{
_election.CreateVote();
_election.VoteNo();
Assert.AreEqual(TransactionElectionResult.FinishedNo, _election.Result);
}
}
[Test]
public void Unanimous_yes_votes_will_commit_the_underlying_transaction()
{
using (Record)
{
_transaction.Commit();
}
using (Playback)
{
_election.CreateVote();
_election.CreateVote();
_election.VoteYes();
Assert.AreEqual(TransactionElectionResult.InProgress, _election.Result);
_election.VoteYes();
}
}
[Test]
public void A_yes_vote_will_leave_the_election_with_a_finished_and_voted_yes_status()
{
using (Record)
{
_transaction.Commit();
}
using (Playback)
{
_election.CreateVote();
_election.CreateVote();
_election.VoteYes();
Assert.AreEqual(TransactionElectionResult.InProgress, _election.Result);
_election.VoteYes();
Assert.AreEqual(TransactionElectionResult.FinishedYes, _election.Result);
}
}
}
There are a couple of things to look for in this sample (and, yes, you'll be able to download it, use it, complain about it, etc. under the MIT license very soon). First, I use the namespace to describe the class that's being specified. I'm a little bothered by this as I feel true BDD would ignore this fact. In a way, these specifications are very TDD-style as they all rally around a single class. To be honest, at this point I'm okay with this. It's as if the TranactionElection class were the direct object in each sentence of specification.
To cut down on line noise I use a base class for my specifications (aka test fixtures). I could take it a bit further with AutoMockingContainer. That's a good change for a rainy day, so, for now, I'm going with a low fidelity technique for making my specs a little bit cleaner. Here's the Spec class:
using System;
using NUnit.Framework;
using Rhino.Mocks;
namespace XEVA.Framework.Specs
{
[TestFixture]
public class Spec
{
private MockRepository _mocks;
[SetUp]
public void MainSetup()
{
_mocks = new MockRepository();
Before_each_spec();
}
[TearDown]
protected void MainTeardown()
{
After_each_spec();
}
public MockRepository Mocks
{
get { return _mocks; }
}
protected virtual void Before_each_spec() { }
protected virtual void After_each_spec() { }
protected IDisposable Record
{
get { return _mocks.Record(); }
}
protected IDisposable Playback
{
get { return _mocks.Playback(); }
}
protected TType Mock<TType>()
{
return _mocks.DynamicMock<TType>();
}
protected TType Partial<TType>()
where TType : class
{
return _mocks.PartialMock<TType>();
}
protected TType Stub<TType>()
{
return _mocks.Stub<TType>();
}
protected void Verify(object mock)
{
_mocks.Verify(mock);
}
protected void VerifyAll()
{
_mocks.VerifyAll();
}
}
}
The main reason I am appropriating the namespace is to receive a specification output that reads easily in tooling. It's a bit easier than using an attribute on each class and you don't really need the namespace for the specifications.
Here's a screen shot from the ReSharper test runner:
Sexy.
I could take my test runner (NUnit) output and easily format a report should that become desirable:
Specifications for TransactionElection
When votes are cast,
a single no vote should rollback the transaction.
a no vote will leave the election with a finished and voted no status.
unanimous yes votes will commit the underlying transaction.
...
All things considered I'm not sure I'm 100% convinced. Let's say ~90%. I know I like the output, but I think reverse engineering TDD-style tests to BDD-style tests have left my contexts (when) a little soft in the middle. I'm going to keep going through a few of the larger fixtures to see if any refactoring patterns or tips emerge and I'll be sure to revisit these tests based on accumulated experience. I will say that I believe it's ok to use technical language for technical/infrastructure classes as evidenced in the presented example.
I am trying to banish the term "test" from my vocabulary. At least as far as "unit tests" or "test-driven development" go. I really see developer testing (there we go again) as a process of leaving a breadcrumb trail of executable specifications. In a domain modeling scenario these tests may/should be business-readable. In the case of a TransactionElection class you're really leaving multiple entry points for other developers to reenter code. This is key to enabling collective ownership.
Look for more posts on this subject (along with a code drop) soon.