Saturday, June 21, 2014

Specs in Scala and C#

"That I have uttered. Bring me to the test,
And I the matter will re-word, which madness
"
-- Shakespeare, Hamlet
Act III, Scene IV, Lines 143-144

Prolog


Places, places, the play is about to begin.

Tonight we shall look at a tale of two specification frameworks, Scala's spec2 and .Net's SpecFlow.

Act I - spec2


Say we have the following basic FizzBuzz functionality in Scala.

object FizzBuzzer {
class FizzBuzzExtractor[T](f: T => Boolean) {
def unapply(x: T) = f(x)
}
val FizzBuzz = new FizzBuzzExtractor[Int](_ % 15 == 0)
val Fizz = new FizzBuzzExtractor[Int](_ % 3 == 0)
val Buzz = new FizzBuzzExtractor[Int](_ % 5 == 0)
def eval(value: Int) = value match {
case FizzBuzz() => "FizzBuzz"
case Fizz() => "Fizz"
case Buzz() => "Buzz"
case _ => value.toString
}
}

How could we go about testing this functionality?  We could do so simple unit testing, but we have business people that really want to make sure that we get this right and they will not be able to follow our unit tests without a bit of help on our part.

This is when a specification framework using a Cucumber or Gherkin style of syntax comes into play.  As DHH pointed out in "Is TDD Dead" part IV, Cucumber is really a pain to use and as such we really need to think about the cost of using this for our testing.

In this made up case we are going to say that the business is ready and willing to fully learn and use the the Given / When / Then style and thinking that Cucumber will force upon them.  In reality I have worked with business partners in which this was the case, others that thought this was the case and then stop doing it, and still others that wanted nothing to do with this "IT thing".  If you are going to do testing in this way, know what you are getting yourself into.

Given all that, let us put together our tests working closely with our business partners.

Note, I am new to Scala, as such I need to look at this gist form Kingsley Davies in order to do the kata.


We see that for this kind of functionality the team as a whole felt that a truth table would be the best way to verify functionality, as such we set up the following truth table in spec2.

def table =
"input" | "result" |>
2 ! "2" |
3 ! "Fizz" |
5 ! "Buzz" |
15 ! "FizzBuzz" |
33 ! "Fizz" |
55 ! "Buzz" |
150 ! "FizzBuzz" |
151 ! "151" |

We go ahead and run the input and expected results through our Scala function using the following.

{ (a, b) => { FizzBuzzer.eval(a) must_== b } }

In this case, in our lambda we are binding a to our input and b to our expected result.  Then we go a head and assert using the must assertion that value which comes out of our FizzBuzzer does indeed equal our excepted result.



The nice thing about setting up a continuous testing environment with sbt using ~test is that you can implement the functionality in a BDD style.



As we see in the example above if we have a failing condition in our truth table we are notified which row in the table is not true (in this case 151).  We are then able to write enough code to pass the failing row and move on.

Act II - SpecFlow


Say we have the following basic FizzBuzz functionality in C#.

public class FizzBuzzer
{
public string Translate(int value)
{
var result = string.Empty;
if (value%3 == 0) result += "Fizz";
if (value%5 == 0) result += "Buzz";
return string.IsNullOrEmpty(result) ? value.ToString() : result;
}
}

Like in Act I our business partners wants to get involved in the verification and validation of this functionality.  As such they are willing and ready to learn the Cucumber syntax and thinking so that the whole team can be on the same page.

Given the nature of the functionality we are developing the team feels that a truth table will be the best way to test it.  As such the following truth table is come up with as a team.
Examples:
| value | expected |
| 1 | 1 |
| 2 | 2 |
| 3 | Fizz |
| 4 | 4 |
| 5 | Buzz |
| 6 | Fizz |
| 7 | 7 |
| 8 | 8 |
| 9 | Fizz |
| 10 | Buzz |
| 11 | 11 |
| 12 | Fizz |
| 13 | 13 |
| 14 | 14 |
| 15 | FizzBuzz |
| 30 | FizzBuzz |
| 45 | FizzBuzz |

In it we have the input and the expected output of the functionality.  We use SpecFlow to come up with the following.



In our given we using the following line to place in the input into memory to be used in the when step.

ScenarioContext.Current.Add("target", new FizzBuzzer());

In our when we are then able to read back out the input from the given step and pass it to our FizzBuzz functionality giving use the actual result which we place into memory to be used in the then step.

var target = ScenarioContext.Current.Get<FizzBuzzer>("target");
var actual = target.Translate(value);
ScenarioContext.Current.Add("value", actual);

Last in our then step we take the actual result and compare it to the expected result, using NUnit to assert that are in fact equal to each other.

var actual = ScenarioContext.Current.Get<string>("value");
Assert.That(actual, Is.EqualTo(expected));

The nice thing about SpecFlow is that you generate the C# coded spec directly from the Cucumber feature file.  The other really nice thing is that it works with NCrunch for continuous testing!

We see below that if we have row not passing in our truth table we get a detail error message stated what is the following row and why.



Once we write just enough code to get back to green we can continue along refactoring and write our next failing testing repeating the red / green / refactor loop.



Act III - Now what


We see that if we, the development team and our business partners, are willing to pay the cost associated with Cucumber we can as a team get everyone on the same page with the same language and understanding of requirements.  Now what?  If you are looking for more information about BDD and SpecFlow check out a colleague of mine, Richard Yu's presentation called "50 Shades of BDD" (he does not know that I am adding him to this blog post, so I hope he does not mind).