Multiple Roles for SpecSalad

I have uploaded a new version of the SpecSalad framework to both the Nuget gallery and Github

The breaking news is the introduction of multiple roles for a scenario, prior to this release SpecSalad could only define a single role for a scenario, with this new release multiple roles can be defined at the start of a scenario, allowing the scenario text to more closely match the business scenarios where more than a single person or process is involved. 

Data and complete objects can be passed between these roles using the currently available save and restore functionality available from the application role base class.

So how does this work? 

Taking the example from the Codebreaker game in a previous post, we can change the codebreaker submits guess scenario to make the intent clearer, currently the text had to say that the codebreaker set the secret code.

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

When in fact is should state that another component sets the code, which the codebreaker is then trying to guess. 

Using the new multiple roles feature the text can now make this separation clear, specifying who is responsible for setting the secret code.

Scenario Outline: submit guess
    Given I am a codebreaker
    And there is a secret code generator
    When the secret code generator did set the code to: <code>
    And I attempt to submit a guess: <guess>
    Then I should see screen text that includes: <mark>

This rewritten scenario now explicitly states how the secret code is set, and that this responsibility is separate from the codebreaker role.

the new role class is created in exactly the same way as before stored within the roles directory and inheriting from the ApplicationRole

  1. public class SecretCodeGenerator : ApplicationRole
  2. {
  3.     public void SetSecretCodeTo(string code)
  4.     {
  5.         var codeGenerator = new Mock<ISecretCodeGenerator>();
  7.         codeGenerator.Setup(x => x.GenerateCode()).Returns(code);
  9.         this.StoreValue("SecretCodeGenerator", codeGenerator.Object);
  10.     }
  11. }

The code here is almost identical to the original code in the codebreaker role, the important change from the original is the last line this stores the code generator mock object so that it can be accessed by the codebreaker role when required.

Several changes have been made to the codebreaker role, after the removal of the secret code generator calls. 

The submit guess method was changed so that it calls the start game before calling the games guess method

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

The “start the game” method was tidied up, removing the creation of the secret code generator mock, moving the call to a property that will return the required secret code generator..

  1. public void StartTheGame()
  2. {
  3.     _screen = new StubScreen();
  5.     _game = new Game(_screen, SecretCodeGenerator);
  7.     _game.Start();
  8. }

The new secret code generator property attempts to retrieve the generator using the application role base class call to retrieve, which returns the generator defined in the secret code generator role, a check is made to ensure we have retrieved a generator before returning it.

  1. ISecretCodeGenerator SecretCodeGenerator
  2. {
  3.     get
  4.     {
  5.         var toReturn = (ISecretCodeGenerator) Retrieve("SecretCodeGenerator");
  7.         if (toReturn == null)
  8.         {
  9.             var mock  = new Mock<ISecretCodeGenerator>();
  10.             mock.Setup(x => x.GenerateCode()).Returns("0");
  12.             toReturn = mock.Object;
  13.         }
  15.         return toReturn;
  16.     }
  17. }

The check for a retrieved value is required because the start game scenarios also call the “start the game” method and because these scenarios have no interest in the secret code they do not define a generator, so to ensure that these scenarios pass a default generator is defined that simply returns zero.

The SpecSalad syntax has been updated allowing scenario step to define a role, and state which role is to carry out the scenario step task.

This new syntax is compatible with scenarios written with a previous version of the framework.


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 Multiple Roles for SpecSalad

  1. I’m really glad to see the progress you’re making on this Duncan! We’ve not got to implementing multiple roles yet.

    We had to solve a similar problem to this before whilst having to work around static typing (although in Java). We did it by creating a “CastingDirector”[1] to manage the “Actors”[2] playing the roles[2]. If there was no Actor for a role, the CastingDirector would instantiate an actor initialised with an instance of the Role (assigned to the Actor’s ‘character’ attribute).

    Once we had an Actor playing a Role, they were held in a DressingRoom – which was just a HashMap using the name of the role (from the scenario) as a key to the relevant Actor instance.

    As a thought experiment – maybe consider how you might get rid of the Role.DoSomething() and replace it with just… DoSomething(). One of the benefits of ruby is using mixins to avoid having to say what thing we’re operating on. I tried to solve a similar problem to that (again in Java) a while back and came up with this post on my old blog

    Links for the following can be found in the (now defunct) Fitnesse Narratives and related projects. Links on this page:
    [1] NarrativeFixture
    [2] JNarrate

    I look forward to seeing how SpecSalad goes.

  2. Thanks for the encouragement, I am simply standing on the shoulders of giants, the original converted beautifully into c#

Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s