.KnowHowTo.EvolveTowardsBusinessSpecificationLanguage

.KnowHowTo.EvolveTowardsBusinessSpecificationLanguage

Part 2: Rick Mugridge, 12 Feb 2011

The first article in this series (.KnowHowTo) discussed how to DesignSpecificationLanguages.

Let's now look at how to take existing storytests and slowly evolve them to use a business-level language.

This involves a process of abstracting from the implementation-language level. In this article, we'll begin to show how to do this in terms of FitLibrary. However, the general ideas also apply to other tools.

Many thanks to Gojko Adzic for so clearly articulating the notion of language layers (see SBE).

1. Why start with the implementation layer?

A better process is to start with designing/evolving the business-layer language and then work down to the implementation layer(s), in an iterative manner. We discuss this approach later in this series.

But many people are not yet able to start at the business layer. Here's some reasons "we" may have for this:

Legacy Systems

Most systems are developed without storytests. So with such a "legacy" system, we need to build up the storytests from what existing people know and from the system itself.

This is harder when the system is older, or more complex, or we don't have good access to people who sufficiently understand the finer details of the business domain.
So we can be very sure about the implementation level, but unsure about the business level. So it makes sense to focus on the implementation level, as that's clear (even though it may be a fragile view).

When we start with a legacy system, we will probably spend more time verifying that our storytests are consistent with the system, instead of vice versa.

2. How to think about it?

With implementation-level storytests, much of the detail is there. But it tends to be verbose, repetitive and not well organised around the business domain.

So the process of evolving the storytests to focus on the business domain is a matter of extracting out the goodness, the essence of what's there. I see this as a process of abstraction and reorganisation.

We can also use the analogy of compressing the storytests by taking out the irrelevant detail - but without losing it!

When we look at an implementation-level storytest, we may notice:
When we "maintain"/alter our storytests, we may notice:

3. How and Where to begin?

It does not make sense to try and fix all our storytests at once. Some will never need to be changed because they involve parts of the system that are stable.

So it's best to focus effort where it will have more value, where things are changing:

4. A Simple Example: Response Examiner

Let's start with a simple example, which comes from the acceptance tests for FitNesse itself. These were written in the early days of FitNesse development.

There are many storytests about making changes to a page. Let's pretend that a storytest for a new type of change is being added, which is a minor variant on what's there already. We could copy and paste an existing storytest to create a new one, and then change it to suit. But let's first quickly review what's there.

We notice one table in particular occurs repeatedly across the storytests, often with a comment before it:


Make sure we get an error message.

Response Examiner.
typepatternmatches?wrapped html?
contentsCannot movetrue


Here's another example:


Check for the proper title.
Response Examiner.
typepatternmatches?wrapped html?
contentsContent Searchtrue


As we look, we find that this pattern is repeated

This pattern is repeated 45 times over the FitNesse storytests, and usually with a comment.

The table is rather obscure, with a focus on implementation. For example, the matches? part is there simply to carry out the action, with a result of true or false, depending on whether the action was carried out or not. (I wonder: "Is false possible here?") The last column is to display the actual contents, which is helpful when things go wrong.

What could we do instead? The business domain of FitNesse is editing pages in a hierarchy and running tests. So we could express the above two examples more simply like this:

page containsCannot Move

page containsContent Search

So we can remove the comment and the table with 9 cells and replace them with a table with 2 cells. The result is clearer and quicker to read. We don't need to explain anything about the implementation level details such as:
In addition, if we want to change the validation, we can do it in one place, rather than having to find all those tables with the same basic structure.

So we could go ahead and change all the storytests to incorporate this change. Unfortunately, that would be rather slow and boring to do it by hand to all 45 storytests. And it would be easy to make mistakes. That's why storytests tend to degrade over time, and then become brittle. Luckily, we have a solution!

Mapping the business language to the implementation language

With FitLibrary in Java, there are two different ways that we could support that new business-level action:

For example, here's the defined action for page contains:

page containscontents

Response Examiner.
typepatternmatches?wrapped html?
contents@{contents}true


The first table is the header, which gives the name and parameter (contents) of the defined action. This follows the DoFixture convention of alternating keywords and parameters. The second table is the body. It makes use of the single parameter, with the @{} notation.

When a storytest that uses a defined action is run (tested), the defined action is expanded within the report if there is an error or if expansion is explicitly requested. So this means that we still have the detail around for when we need it - when things go wrong. Otherwise we can forget about the detail.

In general with defined actions:
We'll see more examples of defined actions in later articles, including multi-defined actions.

5. Refactoring

The change above is a type of refactoring. In general, we may take several tables and collapse them into one. There are several different types of refactoring that we will want to carry out on implementation-level storytests. These will be covered in later articles in this series. For example, at the larger scale, some refactoring will take several separate storytests and collapse them into a single table.

There is good support for refactoring with many programming languages, but very little support for it with storytests. In a later article in this series we'll see how a new open-source tool supports this. As well as being able to carry out refactorings, it can also find repetitions across storytests that point to verbosity and potential abstractions.

6. Questions for you

References


1. Gojko Adzic, Specification by Example (Manning, 2011)