- Posted by liammclennan on March 17, 2010
I have made a change to my code-based BDD style. I start with a scenario such as:
Pre-Editing
* Given I am a book editor
* And some chapters are locked and some are not
* When I view the list of chapters for editing
* Then I should see some chapters are editable and are not locked
* And I should see some chapters are not editable and are locked
and I implement it using a modified SpecUnit base class as:
[Concern("Chapter Editing")]
public class when_pre_editing_a_chapter : BaseSpec
{
private User i;
// other context variables
protected override void Given()
{
i_am_a_book_editor();
some_chapters_are_locked_and_some_are_not();
}
protected override void Do()
{
i_view_the_list_of_chapters_for_editing();
}
private void i_am_a_book_editor()
{
i = new UserBuilder().WithUsername("me").WithRole(UserRole.BookEditor).Build();
}
private void some_chapters_are_locked_and_some_are_not()
{
}
private void i_view_the_list_of_chapters_for_editing()
{
}
[Observation]
public void should_see_some_chapters_are_editable_and_are_not_locked()
{
}
[Observation]
public void should_see_some_chapters_are_not_editable_and_are_locked()
{
}
}
and the output from the specunit report tool is:
Chapter Editing specifications 1 context, 2 specifications
Chapter Editing, when pre editing a chapter 2 specifications
- should see some chapters are editable and are not locked
- should see some chapters are not editable and are locked
The intent is to provide a clear mapping from story –> scenarios –> bdd tests.
- Posted by liammclennan on November 19, 2009
I have written about about WebAii before. It is functional but the API sucks. I have written about NGourd too.
I am currently working on a project that is using the combination of NGourd and WebAiii for automated acceptance testing. We start with a story:
Feature: Search
As a user
I want to search for items
so that I can find data that I am interested in
and then write some scenarios like:
Scenario: Search for a compensation agreement
Given I am at the home page
When I select the Agreements perspective
And I search for 'agreement 1'
Then the search results should be displayed
Within the test project we have the following directory structure:

Search.feature is a text file containing the previously listed feature and scenario definitions. For each scenario step we must have a corresponding step definition. For example the step ‘When I select the Agreements perspective’ matches the following step definition:
[Step(@"search for '([\w\s]+)'")]
public void search_for(string searchTerm)
{
CurrentBrowser.Find.ById("Terms").SetValue("value", searchTerm);
CurrentBrowser.Click(CurrentBrowser.Find.ById("search_submit"));
}
Note the use of regular expressions to parameterise the step. Because this step is an action we put it in the ActionSteps file. Everything that we need to do for our tests falls into one of the three categories: Action, ContentAssertion or Navigation. The goal is to avoid defining the same step twice so that the set of steps form a domain specific language that can be used by business analysts and the like.
NGourd is a Cucumber knockoff, but without many of the features. However, it is surprising how far you can get with just the basics. So far it is working nicely.
- Posted by liammclennan on April 18, 2009

Today I started experimenting with NHibernate. As I have become more interested in TDD, BDD, SOLID, DDD and other upercase acronyms I am incre
asingly seeing NHibernate as the only viable option for persistence. NHibernate will be the third ORM tool that I have learnt. The first two were Wilson ORMapper and Linq-to-sql. Unfortunately for me, both Wilson ORMapper and Linq-to-sql have been killed off by their respective authors. Initially I thought that because I understand ORM tools learning NHibernate would be trivial, but it has turned out to be far more complicated than either Wilson ORMapper or Linq-to-sql.
So far I do not like NHibernate. It is the opposite of convention over configuration. There are ten different ways to do anything, which contributes to its already poor and distributed documentation. It is great to have choices but in the case of NHibernate I think it really weakens the experience. At the very least there should be some kind of out-of-the-box experience that uses a default strategy that just works (ala RoR). Here are some of the areas where a user has to choose how to use NHibernate:
- Mapping Strategy - NHibernate supports mapping by attributes, xml files or a fluent interface. The fluent interface seems to be the way to go but I could not get it to work. Even the demo app on the fluent nhibernate site does not work. Therefore, I went back to the xml mapping strategy.
- Configuration - Configure in code, hibernate.cfg.xml file, app.config or the fluent interface.
- Query Strategy - There are three different techniques for querying: HQL, Criteria (detached and attached) and Linq-to-Nhibernate. From what I can gather Linq-to-Nhibernate is not really production ready. HQL does not have any compile-time checking so I am leaning towards a query strategy based on detached criteria.
- Session Management - I will be using NHibernate in a web environment so I plan to implement the session-per-request pattern. This means that my ISessionFactory will be application scope and ISession will be scoped to an individual HTTP request.
- Architectural Patterns - Ayende's excellent article Repository is the new Singleton describes two different data access patterns that both apply to NHibernate: DAO/Repository and Specification. He contends that DAO services don't provide any value and we should access ISession directly and encapsulate querys into query objects (or specifications). I'm not sure what to do here.
The book, NHibernate in Action is the most useful NHibernate documentation that I have. Its obsolescence shows NHibernate's rapid recent change. Despite the book being only a few months old it is hopelessly out of date. The book documents NHibernate 1.2 but NHibernate 2.0 is the current version. Major features such as fluent nhibernate and Linq-to-nhibernate are not mentioned in the book.
Maybe I'll try the entity framework...
- Posted by liammclennan on March 18, 2009
Since I discovered the beauty and clarity of BDD I have naturally been trying to write all of my tests in the BDD style (establish context, execute, assert observations). What I have found is that this style of testing is not always suitable. Because BDD is expressed from the end-user's view point it is not an efficient way to test component implementation. Doing so necessitates setting up a lot of uneccessary context.
BDD tests are supposed to be expressed from the point of view of the end users of a system. A single end user transaction will often have many complex sub-components involved. Testing the sub-components in the BDD style requires establishing a large number of slightly different contexts. I have found that it is not worth the effort when the sub-components can be tested directly.
My strategy is to use BDD to test the variations that are valid from the users point of view. If I was testing authentication scenarios I would have a BDD test for a successful authentication and a BDD test for failure authentication. Every other scenario, such as testing that authentication fails if the database is unavailable, is a test of the authentication sub component and it should be done in the traditional TDD style.
- Posted by liammclennan on March 5, 2009
Tonight was the second Brisbane Alt.Net meeting. Attendance was WAY down. I guess some people came along last time and discovered it was not for them. :)
We started with a quick look at Sharp Architecture. Since no one has used it that didn't go very far.
Next was our usual, ongoing "discussion' about the merit of various TDD features. Mocking was a particularly contentious technique. When should you mock and when should you stub? This was in conjunction with a lilttle demo I did introducing BDD. I worked through the typical login scenario in the BDD fashion. These are stories we implemented:
As a user
I want to be able to authenticate
so that I can access the applications functionality
As an evil hacker
I want to get rejected by the authentication process
So that I don't spoil it for everyone else
As a user with a "special case" login (not a user in the database)
I want to be able to authenticate
so that I can access the applications functionality
Here is the zip of the solution we ended up with.
Finally we decided that we need to get a bit more organised and publish an agenda in advance of each meeting.
- Posted by liammclennan on February 14, 2009
DISCLAIMER: As the title indicates, I am a BDD beginner. I think it is useful for me to publish my experiences because other beginners may find some benefit. If you see something I am doing wrong please leave a comment for everyone to learn from.
I came across a requirement today that I thought was particularly suited to a BDD intro. The requirement in my bug tracker looks like this:
When viewing vehicle details (tracking screen)
Display the timespan between the vehicle's last log and the current time.
when the timespan is less more than one hour
format the timespan as hh:mm eg 1:22 ago
when the timespan is less than one hour
format the timespan as mm ago eg 21 minutes ago
when the timespan is less than one minute
format the timespan as less than 1 minute ago
Tweeple will recognise the similarity between these requirements and the way that twitter shows the age of tweets.
The BDD test, using SpecUnit.net, for one of these requirements might look like this:
[Concern("Vehicles"), TestFixture]
public class when_viewing_vehicle_details_with_last_log_more_than_one_hour_ago
: ContextSpecification
{
/* Any objects required by the test */
protected override void Context()
{
// setup and execute
}
[Observation, Test]
public void should_format_the_timespan_as_hhmm_ago()
{
// verify requirement here
}
}
The name of the class is the context. This is analogous to a use case or a user story. The name of the test method (or BDD observation) is what we are asserting.
In this case the requirements are implemented by a controller action method called VehicleDetail. Here is my complete BDD test:
[Authorize]
public ActionResult VehicleDetail(string vehicleCode)
{
var status = vehicleService.RetrieveVehicleStatus(vehicleCode);
if (status == null) return Content("No data.");
return View("Detail", status);
}
So to complete the test I need to mock the vehicle service:
[Concern("Vehicles"), TestFixture]
public class when_viewing_vehicle_details_with_last_log_more_than_one_hour_ago : ContextSpecification
{
private VehicleController controller;
protected AutoMockContainer container =
new AutoMockContainer(new MockFactory(MockBehavior.Loose));
private const string VEHICLE_CODE = "IMAVEHICLE";
private VehicleStatusDTO status;
private ViewResult result;
private DateTime LOG_TIMESTAMP = new DateTime(2009, 2, 10, 10, 30, 0);
private DateTime CURRENT_TIME;
protected override void Context()
{
CURRENT_TIME = LOG_TIMESTAMP.AddHours(2).AddMinutes(6);
status = new VehicleStatusDTO(
new VehicleStatus(new VehicleBuilder().Build(),
new LogBuilder()
.WithDeviceTimeStamp(LOG_TIMESTAMP).Build(), Driver.DefaultDriver),
new DeviceBuilder().Build(), CURRENT_TIME);
container.GetMock<ivehicleservice>()
.Expect(vs => vs.RetrieveVehicleStatus(VEHICLE_CODE))
.Returns(status);
controller = container.Create<vehiclecontroller>();
result = (ViewResult) controller.VehicleDetail(VEHICLE_CODE);
}
[Observation, Test]
public void should_format_the_timespan_as_hhmm_ago()
{
var dto = (VehicleStatusDTO)result.ViewData.Model;
Assert.AreEqual("2:06 ago", dto.TimeAgo);
}
}
SpecUnit.Net includes a tool (SpecUnit.Report.exe) that generates a html report of the specifications in an assembly. The final report, for the Vehicles context, looks like this:
Vehicles specifications 6 contexts, 6 specifications
Vehicles, when viewing vehicle details with last log more than one hour ago 1 specification
- should format the timespan as hhmm ago
Vehicles, when viewing vehicle details with last log more than one and less than two days ago 1 specification
- should format the timespan as 1 day ago
Vehicles, when viewing vehicle details with last log more than two days ago 1 specification
- should format the timespan as X days ago
Vehicles, when viewing vehicle details with last log less than one hour ago 1 specification
- should format the timespan as X minutes ago
Vehicles, when viewing vehicle details with last log between one and two minutes ago 1 specification
- should format the timespan as 1 minute ago
Vehicles, when viewing vehicle details with last log less than one minute ago 1 specification
- should format the timespan as X minutes ago