I prefer to design my code by specifying expected behavior before any code is written. Machine.Specifications (mspec) provides a great way to accomplish this with less overall code and the bonus of normal-person-readable specifications. BDD keeps me focused on what the code is actually supposed to do, not on the classes and methods themselves. To exercise my BDD muscles, I decided to work through the popular “String Calculator Kata” by Roy Osherove. His instructions were from the traditional TDD approach, though, so I took a stab at converting them to a simple user story with acceptance criteria. Here’s what I came up with:

“As a user, I want to add numbers together so that I can know the sum.”

WHEN provided an empty string,
THEN it should return 0

WHEN provided one number,
THEN it should return the same number

WHEN provided two numbers separated by a comma,
THEN it should return the sum of those two numbers

WHEN provided an unknown amount of numbers separated by commas,
THEN it should return the sum of all numbers provided

WHEN provided numbers with a new line character,
THEN it should return the sum of all numbers provided

WHEN provided numbers separated by a specified delimiter,
THEN it should return the sum of all numbers provided

WHEN provided one negative number,
THEN it should throw an exception (“Negative numbers are not allowed – [Display the negative number.”)

WHEN provided more than one negative number,
THEN it should throw an exception (“Negative numbers are not allowed – [Display the negative numbers.”)

So, let's dive in and see where the story leads us! My job from here on out is to create a spec based on a single acceptance criterion, run the spec so that it fails, write JUST ENOUGH code in my new string calculator class to make the spec pass, and then move on to the next acceptance criterion.

Acceptance Criterion:

WHEN provided an empty string,
THEN it should return 0

Specification with mspec:

Resulting code:

Acceptance Criterion:

WHEN provided one number,
THEN it should return the same number

Specification with mspec:

Now, I’m starting to see some repetition in my context. Before moving forward, I’m going to refactor my specs a bit to use a common context. For this, I will use an abstract class that starts with the word “given”.

So, first spec looks like this:

…and my second spec looks like this:

Resulting code:

Acceptance Criterion:

WHEN provided two numbers separated by a comma,
THEN it should return the sum of those two numbers

Specification with mspec:

Resulting code:

Acceptance Criterion:

WHEN provided an unknown amount of numbers separated by commas,
THEN it should return the sum of all numbers provided

Specification with mspec:

UH OH! This spec passed without any modification to the calculator. I don’t like when this happens, but I don’t think I could have made the Add method much simpler when adding two numbers separated by a comma. Now, because of my use of Linq’s Sum extension method, it passes with any amount of numbers. Moving on…

Acceptance Criterion:

WHEN provided numbers with a new line character,
THEN it should return the sum of all numbers provided

Specification with mspec:

Resulting code:

Acceptance Criterion:

WHEN provided numbers separated by a specified delimiter,
THEN it should return the sum of all numbers provided

Specification with mspec:

Resulting code:

Acceptance Criterion:

WHEN provided one negative number,
THEN it should throw an exception (“Negative numbers are not allowed – [Display the negative number.”)

Specification with mspec:

Resulting code:

Acceptance Criterion:

WHEN provided more than one negative number,
THEN it should throw an exception (“Negative numbers are not allowed – [Display the negative numbers.”)

Specification with mspec:

Resulting code:

Here are the results from all eight specs:

[code]
when provided more than one negative number
» should throw an exception
when provided one negative number
» should throw an exception
when provided numbers separated by a specified delimiter
» should return the sum of all numbers provided
when provided numbers with a new line character
» should return the sum of all numbers provided
when provided multiple numbers separated by commas
» should return the sum of all numbers provided
when provided two number separated by a comma
» should return the sum of those two numbers
when provided one number
» should return the same number
when provided an empty string
» should return zero

8 passed, 0 failed, 0 skipped, took 1.05 seconds (Machine.Specifications 0.3.0).
[/code]

In a simple kata like this, the end-result is pretty similar to what might come out of a traditional TDD process. Imagine doing a much more complicated code project, and you’ll probably begin to see how powerful and valuable Behavior-Driven Development can be.

Download Source Code

  • http://aspiringcraftsman.com Derek Greer

    Great post Byron! For what it’s worth, the issue with your passing sum of multiple numbers spec is really with the acceptance criteria. Returning the sum of two numbers separated by commas is just an instance of the ability to return the sum of an unlimited string of numbers separated by commas, so that’s all that needed to be specified.

    • Byron Sommardahl

      Thanks Derek! I believe you’re right. It looks like the 3rd and 4th AC’s can be condensed into something like, “WHEN provided two or more numbers separated by commas, THEN it should return the sum of those numbers.” Good catch!

  • DavidS

    This is an excellent up to date post on how to actually get started with writing MSpec code! Thanks a lot for taking the time to share the article and the code.

    • Anonymous

      Thanks David! Are you planning on or already using MSpec?