Contract Tests with Slim/Fitnesse : Running a test suite against multiple implementations of an interface

Recently I had a need for something similar to "xUnit contract tests" in Fitnesse (defined as: a test suite, which any object must satisfy so that it can be considered as a valid implementation of a Role. I got the term from one of J.B.Rainsberger's talks. )

To illustrate my point, let me cook up an example.

Imagine a contest for BoneyM impersonators. We would quiz each impersonator with a set of questions that they must answer correctly to win the contest.

i.e. we have a set of tests (questions) for each implementation of an Impersonator (Role). We also have multiple impersonators that I want to validate against the contract tests. The tests need to be dry - since they don't change for each implementation.

So here are the questions and the expected answers to them. (the contract tests)

  • Q:How did rasputin die? A:His enemies shot him till he was dead.
  • Q:Was yesterday sunny? A: No
  • Q:Who runs the baker family? A: Ma Baker
So the contract for each impersonator is

public interface BoneyMImpersonator
string HowDidRasputinDie();
bool WasYesterdaySunny();
string WhoRunsTheBakerFamily();

The new addition to the Fitnesse/Fit family is the SLIM runner. From what I've seen in a couple of days - I'm impressed!
For some quick help to get it running, check out this post by Brett Schuchert -

One of the table-styles that SLIM has introduced is "scenario tables". The thing that is great about them - is that it brings something similar to C++ macros (expansion via text substitution) to Fitnesse. i.e. you can write up a scenario table once and then call it from multiple places in other tables (script/decision tables). Let's see it in action.

Here's my Fitnesse page:

And here's the backing fixture and domain code

Refer to this fitnesse doc page for a sample

There's a collapsed section called "No Peeking" - but you should. Without it, SLIM doesn't have an actor or co-ordinator to talk to - and you'd get cryptic errors like 'Method [name] not found in fitSharp.Slim.Operators.ExecuteBase+NullInstance'. The missing ingredient for me was the |script|quiz| line. With this, you tell Fitnesse that a class named Quiz is who you should talk to.

Next you define the scenario (see the above link for details). In short, a scenario has a name and parameters. Parameters must be used within the scenario prefixed with a @ sign. In our scenario, we use the fully-qualified type name of the implementation as the parameter. In welcome, we create an instance of it via some reflection/activation magic. Next we interrogate the object and compare answers.

Finally we need the scenario to be run against multiple implementations of the contract. For that we have a decision table (equivalent to Fit's ColumnFixture), which must have the exact same name as the scenario and the column headers must match the scenario parameters.
So in effect, each row is replaced by a scenario invocation with the parameters specified in the row. This can be seen when you run the tests. You can see new column with cells, which be expanded to see the scenario details as shown below...

In the future, if you have a new implementation say StonedGuy, just add another row to the Decision table and that's it! The contract tests are DRY-compliant.

Update: Gregor Gramlich points out that the welcome method doesn't need to be implemented in the fixture, just for an instance of the test subject via reflection. Instead a line |start | FullyQualifiedClassName | works just as well. Nice tip. Code samples updated.

No comments:

Post a Comment