SpecSalad Part 2

In the last post I completed the start game feature, In this part I am going to be working on the submit guess feature.

  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 screen text that includes: <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 | 5124  | +–  |
  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  | —- |

Running the first scenario of the submit guess feature throws this error

image

I create the task SetTheCodeTo, this tells the role to set the secret code to a given value

  1. public class SetTheCodeTo : ApplicationTask
  2. {
  3.     public override object Perform_Task()
  4.     {
  5.         var code = Details.Value();
  6.  
  7.         Role.SetSecretCodeTo(code);
  8.  
  9.         return true;
  10.     }
  11. }

The details object accessed from the ApplicaitonTask base class contains all the parameters supplied in the scenario step, in this case there is just a single value, so I can use the Value method that returns the first value in the details, if there was more than a single value, they would be defined in the scenario step with keys for example

  1. When I attempt to subtract: the number '<subtractor>' from the number '<subtractee>'

in this case I would use the Value_Of method of details which requires a key, in the above example the first value has the key “the_number” the second value has the key “from_the_number”.

The role method SetSecretCodeTo after some refactoring simply calls the start game method with the required secret code

  1. public void StartTheGame(string code = "0")
  2. {
  3.     _screen = new StubScreen();
  4.     var codeGenerator = new Mock<ISecretCodeGenerator>();
  5.  
  6.     codeGenerator.Setup(x => x.GenerateCode()).Returns(code);
  7.  
  8.     _game = new Game(_screen, codeGenerator.Object);
  9.  
  10.     _game.Start();
  11. }
  12.  
  13. public void SetSecretCodeTo(string code)
  14. {
  15.     StartTheGame(code);
  16. }

The start game method has been changed to accept a secret code which is defaulted to zero using a net 4.0 optional parameter, this value is returned by the mock secret code generator, finally the start method is called which in the game class will call the generator and store its returned value.  Running the tests again, I get the following error

image

The new submit a guess task, gets the guess from the scenario step using the value method, and hands it onto the role submit guess method.

  1. public class SubmitAGuess : ApplicationTask
  2. {
  3.     public override object Perform_Task()
  4.     {
  5.         var guess = Details.Value();
  6.  
  7.         Role.SubmitGuess(guess);
  8.  
  9.         return true;
  10.     }
  11. }

 

The role submit guess method has to simply call the games guess method and hand in the players guess.

  1. public void SubmitGuess(string guess)
  2. {
  3.     _game.Guess(guess);
  4. }

I have already refactored the feature to use the same view screen task

  1. Scenario Outline: submit guess
  2.     Given I am a codebreaker
  3.     And I did set the code to, <code>
  4.     When I attempt to submit a guess: <guess>
  5.     Then I should see screen text that includes: <mark>

so when I run the acceptance tests I get the expected failure

image

The error occurs because nothing has been written in the submit guess method, so the screen output collection only includes the lines added during the game start, to get this scenario to pass I added a call to writeline with an empty string in the game guess method.

  1. public void Guess(string guess)
  2. {
  3.     _output.WriteLine("");
  4. }

I now have my first passing test, there is no more code to be written in the acceptance tests,  and getting them all to pass now requires the logic to be added to the games guess method, this is going to be more complex than a simple one line change and so I will create a unit test project, and using this finer grain of testing, drive out the required functionality so that all the acceptance tests pass. 

The features I have written for my game form the framework within which I write the production code, and where required unit tests.  how do I decide when unit tests are required? 

If I can’t make the scenario step pass with a simple line of code, either new code or calling an already established method, then unit tests are required, for example I didn’t feel the need to unit test any of the game start code, as this was almost all to do with writing out to the screen, simple calls to WriteLine, however the calls to guess now need some logic to mark the guess against the secret code so I will unit test this, but always with a view to getting the scenario steps to pass, working from the most simple case towards the most complex.

In the future when I look at the unit tests I should be able to see the flow back towards the feature that caused the unit tests existence, using this technique I have found that the tests become more focused, they become the scaffolding within which I am writing my production code, and although there are fewer, they cover the code better and allow for the codebase to expand and change in the future.

Spec Salad has allowed me to avoid the monolithic step definition file which can be difficult to navigate, instead replacing it with a roles and tasks that are directly linkable to the feature text, more focused and easier to navigate in the future. 

The project layout for code breaker also shows how the split between features, roles and tasks helps with the reading of the codebase, all the actual doing code is contained within the roles, whilst the tasks are responsible for calling the correct role method with the required parameters.

image

The full source code for the code breaker project is available on Github, along with the source for SpecSalad.

SpecSalad is also available as a Nuget package, this will automatically add SpecSalad, SpecFlow and NUnit to the project along with the required configuration in the projects app.config.

If this style of behaviour driven design is of interest then I can recommend the RSpec book, although the language used is Ruby the techniques taught are universal.

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 Uncategorized. Bookmark the permalink.

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