SpecSalad Part 1

I have updated SpecSalad and also at last pushed it up to the Nuget gallery, when it is installed, it also installs SpecFlow and NUnit, along with adding a reference to the spec salad dll  in the step assemblies node of the application config, so it is ready for use.

SpecSalad is a c# implementation of the RiverGlide CukeSalad framework, which is designed to eliminate monolithic step definition class files associated with the standard spec flow style of development.  In many projects there comes a point where finding the way around the various step definitions is done more on experience of where the actual method is within the file rather than any specific layout or pattern! Spec Salad uses Roles and Tasks present an easy and descriptive layout, that makes moving between the feature text, test code and implementation easier.

How does the framework look in a solution? Inspired by the great RSpec Book lets make the game code-breaker! to show the framework in action.

I have uploaded the code to GitHub, The important part to aid navigation is the layout of the test project, in this I create folders that will contain features, roles and the tasks.

image

I start with the features, these define what I am trying to do, and provide me with a simple definition of what done will look like.

  1. Feature: Code-breaker starts game
  2.     As a code-breaker
  3.     I want to start a game
  4.     So that I can break the code
  5.  
  6. Scenario: start game
  7.     Given I am a codebreaker
  8.     When I attempt to start the game
  9.     Then I should see screen text that includes: 'Welcome to Codebreaker!'
  10.     And I should see screen text that includes: 'Enter guess:'

 

  1. Feature: Code-breaker submits guess
  2.     The codebreaker submits a guess of four numbers.
  3.     The game marks the guess with + and – signs
  4.  
  5.     For each number in the guess that matches the number and
  6.     position of a number in the secret code, the mark includes one
  7.     + sign.
  8.     
  9.     For each number in the guess that matches the number but not the
  10.     position of a number in the secret code, the mark inclues one
  11.     – sign.
  12.  
  13. Scenario Outline: submit guess
  14.     Given I am a codebreaker
  15.     And I did set the code to, <code>
  16.     When I attempt to submit a guess: <guess>
  17.     Then I should see on the screen '<mark>'
  18.  
  19.     Scenarios: no matches
  20.     | code | guess | mark |
  21.     | 1234 | 5555  |      |
  22.     
  23.     Scenarios: 1 number correct
  24.     | code | guess | mark |
  25.     | 1234 | 1555  | +    |
  26.     | 1234 | 2555  | –    |
  27.  
  28.     Scenarios: 2 numbers correct
  29.     | code | guess | mark |
  30.     | 1234 | 5254  | ++   |
  31.     | 1234 | 5154  | +-   |
  32.     | 1234 | 2545  | —   |
  33.  
  34.     Scenarios: 3 numbers correct
  35.     | code | guess | mark |
  36.     | 1234 | 5234  | +++  |
  37.     | 1234 | 5134  | ++-  |
  38.     | 1234 | 5134  | +–  |
  39.     | 1234 | 5123  | —  |
  40.  
  41.     Scenarios: all numbers correct
  42.     | code | guess | mark |
  43.     | 1234 | 1234  | ++++ |
  44.     | 1234 | 1243  | ++– |
  45.     | 1234 | 1423  | +— |
  46.     | 1234 | 4321  | —- |

They look like fairly standard SpecFlow features, which they are, only the syntax of each of the scenario steps is important to the spec salad framework.

The currently allowable syntax is

Given [I am | you are] a <role>

Given [I | you] [attempt to | was able to | were able to | did] <task>[: | ,] <parameters>

When [I |you] [attempt to | was able to | were able to | did] <task>[: | ,] <parameters>

Then [I | you] should <question> ‘<answer>’

Then [I | you] should <question> that includes: <answer>

Then [I | you] should <question>

Starting with the first feature, which requires that the game displays a title and prompt at start up, running the feature, I get the following error.

image

The error states I need to create a role or task called ‘codebreaker’, once I have created a class in the roles folder, that inherits from the ApplicationRole base class, and rerun the feature again I get a new error.

image

the error raised this time requires a role or task called “StartTheGame”,  this is the task that the role will carry out.

I created the class in the tasks folder, this class inherits from the ApplicationTask base class, which requires me to implement the PerformAction method, in this method, I tell the role what to do, in this case to start the game.

  1. public class StartTheGame : ApplicationTask
  2. {
  3.     public override object Perform_Task()
  4.     {
  5.         Role.StartTheGame();
  6.  
  7.         return true;
  8.     }
  9. }

 

SpecSalad defines the Role as dynamic, which means that this task will work for any role that implements a StartTheGame method that takes no parameters, so tasks are fully re-useable between different roles. 

Because role is stored in the scenario context of SpecFlow any variables set within the role will be preserved throughout the context without any extra work.

The Role now has to implement the StartTheGame method, and do something, in this case create the game class and call its start method

  1. public class Codebreaker: ApplicationRole
  2. {
  3.     public void StartTheGame()
  4.     {
  5.         var game = new Game();
  6.  
  7.         game.Start();
  8.     }
  9. }

running the test again I get a new failure, stating that I need to create a task ‘SeeTheScreen’

image

Again this is a task, that is going to tell the role to look at the screen and report what it sees, the task will then send this report to the system, which will compare it against the the expected screen text proposed in the scenario definition.

  1. public class SeeScreenText: ApplicationTask
  2. {
  3.     public override object Perform_Task()
  4.     {
  5.         return Role.LookAtOutput();            
  6.     }
  7. }

 

The system comparer for includes syntax expects an IEnumerable to compare against, which the role method LookAtOutput will return. 

The role of cause cannot actually look at the screen, but there are various ways I can mock or stub looking at the standard output, I have chosen what I think is the simplest, an interface that will  implement a WriteLine method, and a stub class that implements this interface and stores each message in a collection.  This collection can then be returned by our role.

The final code looks like this.

  1. public class Codebreaker: ApplicationRole
  2. {
  3.     IOutput _screen;
  4.  
  5.     public void StartTheGame()
  6.     {
  7.         _screen = new StubScreen();
  8.         var game = new Game(_screen);
  9.  
  10.         game.Start();
  11.     }
  12.  
  13.     public IEnumerable<string> LookAtOutput()
  14.     {
  15.         return ((StubScreen)_screen).ReadMessages;
  16.     }
  17. }
  18.  
  19. public class StubScreen : IOutput
  20. {
  21.     readonly IList<string> _readMessages = new List<string>();
  22.  
  23.     public void WriteLine(string message)
  24.     {
  25.         _readMessages.Add(message);
  26.     }
  27.  
  28.     public IEnumerable<string> ReadMessages { get { return _readMessages; } }
  29. }

This gives us our first truly failing test

image

To make this scenario pass I now simply need to write the two expected output lines within the start method.

  1. public class Game
  2. {
  3.     readonly IOutput _output;
  4.  
  5.     public Game(IOutput output)
  6.     {
  7.         _output = output;
  8.     }
  9.  
  10.     public void Start()
  11.     {
  12.         _output.WriteLine("Welcome to Codebreaker!");
  13.         _output.WriteLine("Enter guess:");
  14.     }
  15. }

 

Running the tests again we now get a pass.

image

This looks like a good place to break this already very long post, ready to continue next time with the implementation and testing of the submit guess feature.

Advertisements

About Duncan Butler

Trying to be a very agile software developer, working in C# with Specflow, Nunit and Machine Specifications, and in the evening having fun with Ruby and Rails
This entry was posted in Programming and tagged . Bookmark the permalink.

2 Responses to SpecSalad Part 1

  1. Pingback: Multiple Roles for SpecSalad | The Butler Did It

  2. Mike Hanson says:

    Duncan.

    I’m really finding SpecSalad useful and it has made a huge difference to the cleanliness of my AT code but I have hit a rather frustratiing limitation in the Then syntax.

    I am using it with Watin to test an ASP.NET MVC web app and find finishing many of my scenarios with something like this

    Then I should see a message containing ‘Congratulations, welcome to VRC.’

    At the moment as far as I can see I have to write a separate task for each variation of this because the expected answer element is not passed to the task.

    What I would really like to do is create a SeeAMessageContaining task and have ‘Congratulations, welcome to VRC.’ passed to the Perform_Task method (via Details?) so that I can use Watin’s features to search for the answer text, much like I can for the Given and When steps.

    I’ve tried variations of the permitted Then syntax to get something like this working but it seems that all three veriants require the task to know the expected answer rather than be given it, which makes for a lot of tasks that only differ in the expected answer.

    is there a way to achieve this or possibly a chance of adding this feature?

    Mike

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s