BT

Using Coding Katas, BDD and VS2010 Project Templates: Part 2

Posted by Jamie Phillips on Sep 29, 2010 |

This is the second of a three-part series on how Jamie Phillips used a combination of coding katas, behavior driven development, and project templates to improve his development practices. In this part Jamie introduces the reader to behavior driven development and explains how it can improve the effectiveness of unit testing.

I’m good, but couldn’t I be better?

Part 2: Behavior Driven Development

Test Driven Development overcomes many of the issues faced by development teams that often leave the creation of Unit Tests until after the implementation. With TDD the tests are thought out and created as the code is implemented. Behavior Driven Development goes further by directly associating unit tests (and test cases) with the requirements via the use of natural language. So what is the end result? Simply put – Test Cases and Unit Tests that make sense to everyone in the team; from requirements analyst to tester and developer.

From Part 1 that dealt with Coding Katas, we continue on my journey of discovery into BDD. Having done the Bowling Kata for several days I started to realize that I could concentrate on the actual style of coding and how best to refactor rather than the problem itself. This is where the BDD part dropped in. Although I had heard and read about BDD I had never had the opportunity to actually put it into use; because as I had alluded to before, a lot of my unit testing was based on production code and the opportunities to start “trying out” concepts were never really at the appropriate time. So it was that I was in the midst of doing my Kata trying to remember what David and Ben had been doing during the session when I realized that David’s use of MSpec (Machine Specification – a context/ specification Framework for .NET) was the introduction to BDD that I needed. David has used the MSpec assembly as a means to utilize the format of BDD in his unit tests and in this form it is heavily reliant on being used as a plug-in to ReSharper. Due to my experience and involvement with Build systems, I am grounded by the fact that whatever I do on my machine I need to be able to do on a Build Machine. The approach of using a plug-in to ReSharper was not really an option as it would not work on a machine that did not have Re-Sharper installed.

Behavior Driven Development

Behavior Driven Development is an Agile software development technique that tightly couples the definition of use cases with tests cases and unit tests through close collaboration between the requirements analyst, software tester and software developer.
Typically the Business requirement is written and the team will drill down to flesh out the use cases that will drive the tests cases, the unit tests and ultimately the implementation code. BDD is considered as a step further than TDD (Test Driven Development) and utilizes the test first ideology to the extent that the expected outcome is understood and defined, before the implementation is written.

At the root of Behavior Driven Development is the intent that Developers, Testers and Non-Technical or Business Participants can collaborate on the design of software through the use of natural language from definition to test to implementation. At the Unit Test level this has a profound effect on not only how the test code is written but also the naming convention of the test classes and methods. Consider how the following Test Class and the test method is implemented

[TestClass]
public class ItemLoadTests
{
    [TestMethod]
    public void TestLoadNullCustomer()
    {

      // Arrange
      // Create the stub instance

      INorthwindContext context = MockRepository.GenerateStub<INorthwindContext>();
      //IObjectSet<Customer> customers = new MockEntitySets.CustomerSet();
      IObjectSet<Customer> customers = TestHelper.CreateCustomerList().AsObjectSet();

      // declare the dummy ID we will use as the first parameter
      const string customerId = "500";

      // declare the dummy instance we are going to use
      Customer loadedCustomer;

      // Explicitly state how the stubs should behave
      context.Stub(stub => stub.Customers).Return(customers);

      // Create a real instance of the CustomerManager that we want to put under test
      Managers.CustomerManager manager = new Managers.CustomerManager(context);

      // Act
      manager.Load(customerId, out loadedCustomer);

      // Assert
      context.AssertWasCalled(stub => { var temp = stub.Customers; });
      // Check the expected nature of the dummy intance
      Assert.IsNull(loadedCustomer);
    }
}

You will notice that it is very much in the AAA format of Unit Test (Arrange, Act and Assert), along with the fact that the name of the Test method makes perfect sense to the developer / tester writing it – after all we are testing what happens when we load a Null Customer.

As an aside, in this example I am using the RhinoMocks mocking framework to create the mocked instances of my EntityFramework context interface INorthwindContext, this is not to be confused with the reference to the BDD use of Context later on.

From a BDD point of view we ask ourselves, is that really the intent of the functionality? Perhaps the use case that was written for this particular scenario was more like:

In the context of the NorthWind Customer Manager; when a Customer ID that does not exist in the system is used to load a Customer detail, then a Null instance should be returned.

The previous test for a Null Customer was intended to prove that use case (however fictitious it maybe) and this it did well. Unfortunately the syntax and the context are lost to the casual observer.

Take the same underlying instances and drive the test from the Use Case point of view and you get quite a different picture. The first class is setting up a Base Context that can be used in further scenarios, this in itself is derived from the ContextSpecification class definition that Eric Lee prepared when investigating BDD for MSTest.

/// <summary>
/// Base Context class for CustomerManager Testing
/// </summary>
public class CustomerManagerContext : ContextSpecification
{
   protected INorthwindContext _nwContext;
   protected IObjectSet<Customer> _customers;
   protected string _customerId;
   protected Customer _loadedCustomer;
   protected Managers.CustomerManager _manager;

   /// <summary>
   /// Prepare the base context to be used by child classes
   
/// </summary>
   protected override void Context()
    {
      // Create the stub instance
        _nwContext = MockRepository.GenerateStub<INorthwindContext>();

        _customers = TestHelper.CreateCustomerList().AsObjectSet();

      // Create a real instance of the CustomerManager that we want to put under test
        _manager = new Managers.CustomerManager(_nwContext);
    }

The next section of code is where the actual test class (derived from our CustomerManagerContext above) implements the auxiliary and Test methods:

/// <summary>
/// Test class for CustomerManager Context 
/// </summary>
[TestClass]
public class when_trying_to_load_an_employee_using_a_non_existent_id : CustomerManagerContext
{
    /// <summary>
    /// The "Given some initial context" method
    
/// </summary>
    protected override void Context()
    {
         base.Context();
        _customerId = "500";

         // Explicitly state how the stubs should behave
        _nwContext.Stub(stub => stub.Customers).Return(_customers);
    }

     /// <summary>
     /// The "When an event occurs" method
     
/// </summary>
     protected override void BecauseOf()
    {
        _manager.Load(_customerId, out _loadedCustomer);
    }

     /// <summary>
     /// The "then ensure some outcome" method.
     
/// </summary>
    [TestMethod]
     public void the_employee_instance_should_be_null()
    {
        _nwContext.AssertWasCalled(stub => { var temp = stub.Customers; });
     // Check the expected nature of the dummy intance
        _loadedCustomer.ShouldEqual(null);
    }
}

You will see straight away that the naming convention of the class and the method is not the same as the previous example,by removing the underlines ( _ ) we end up with human readable results, especially when you compare them as follows:

Ok, so the naming convention is not the only difference... Although there maybe a little more overhead with regards to overriding methods, it is important to keep the test writer concentrated on what is happening.

The Context method is akin to the Arrange in our original Unit Test, but here we are concentrating on it in isolation – ensuring that it is all we are going to do.

The BecauseOf method is akin to the Act in our original Unit Test, and here again we see that it is segregated into its own area to ensure that we concentrate on the causality of what we are testing – i.e. we should get a result Because we did something.

Finally, the actual MSTest TestMethod itself is the result – the “should happen” if you like; which is akin to the Assert in the original Unit Test. So from the Unit Test perspective we see that BDD capitalizes on TDD and pushes it further to relate it to the Use Cases that we should be concerned about.

So if we revisit the Bowling Kata I did earlier, we go from Test Methods that have the following format [Arrange – Act – Assert]:

/// <summary>
/// Given that we are playing bowling
/// When I bowl all gutter balls
/// Then my score should be 0
/// </summary> [TestMethod] public void Bowl_all_gutter_balls() {    // Arrange
   // Given that we are playing bowling
   Game game = new Game();    // Act
  // when I bowl all gutter balls
   for (int i = 0; i < 10; i++)    {         game.roll(0);         game.roll(0);    }    // Assert
   // then my score should be 0
  Assert.AreEqual(0, game.score()); }

To Test Methods that have the following format [BDD]

[TestClass]
public class when_bowling_all_gutter_balls : GameContext
{
     /// <summary>
     /// The "When an event occurs" method
     /// </summary>      protected override void BecauseOf()      {
      for (int i = 0; i < 10; i++)
         {
             _game.Roll(0);
             _game.Roll(0);
         }
     }
  
     /// <summary>
     /// The "then ensure some outcome" method.
     /// </summary>      [TestMethod]      public void the_score_should_equal_zero()      {          _game.Score().ShouldEqual(0);      } }

And from Test Results like this:

To Test Results like this:

Granted that the examples seen here are based on very simplistic use cases, but that further drives home the point that the more complex the Use Case the clearer it becomes that there will be issues testing it and therefore the case (no pun intended) can be made to break down the Use Case further.

Next week Jamie Phillips concludes his three-part series by showing how VS2010 project templates can be used to remove the repetitiveness of setting up test cases and projects.

Hello stranger!

You need to Register an InfoQ account or to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Naming tests by Esko Luontola

In your BDD-ified version of the bowling game kata, the test names do not provide any more information than the original tests did. I wrote about this same subject some time ago at blog.orfjackal.net/2010/02/three-styles-of-nami... and there I named the bowling tests like this:


The game has 10 frames

In each frame the player has 2 opportunities (rolls) to knock down 10 pins

When the player fails to knock down some pins
- the score is the number of pins knocked down

When the player knocks down all pins in two tries
- he gets spare bonus: the value of the next roll

When the player knocks down all pins on his first try
- he gets strike bonus: the value of the next two rolls

When the player does a spare or strike in the 10th frame
- he may roll an extra ball to complete the frame


My knowledge of bowling rules is limited, so the above test names probably have much room for improvement.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

1 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2013 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT