Earlier this week, I wrote about some ideas I have for a Specification by Example tool. I have also been working on a prototype for said tool for the last few days. Today I want to show you how said tool should work. Some implementation details shown here will probably change in later prototypes, but the workflow will stay roughly the same.

The tool should be a wiki, just like FitNesse. So, when you create a new page, you have to enter the title of the page and the page content:

creating wiki content

As soon as you enter the keyword “{table}”, the wiki will split the text field at the position of the keyword and show you a specialized table editor in between. You have to enter a table headline. After this, you can enter zero or more table parameters. These table parameters are “given” values - values that should apply to all examples (i.e. rows) in the table. Then you can add an arbitrary number of columns and rows. You can also enter a description for every row, but this is not shown in the following illustration.

creating tabular content

When you save the page, the wiki creates (and compiles) two Java classes: "AutomaticReorderBasedOnReorderLimit", a class that represents the wiki page and "ExamplesOfAutoReordersBasedOnItemsInStock", a class that represents the table. The class names are generated from the headline of the page and the headline of the table.

ExamplesOfAutoReordersBasedOnItemsInStock

public class ExamplesOfAutoReordersBasedOnItemsInStock extends TabularExample {
    public void initialize(final String theReorderLimit) {
        super.initialize(new TabularExample.ExampleInitializer() {
            public void initialize() {
                TheTestTable.this.theReorderLimit = theReorderLimit;
            }
        });
    }

    public TabularExample.ExampleRunner tableRow(final int itemsInStock, 
            final int nextSale, final boolean shouldReorder) {
        return new TabularExample.ExampleRunner() {
            public void run() {
                throw new InconclusiveExampleException(
                    "Inconclusive: This example is not yet implemented.");
            }
        };
    }
}

The values for the “given” parameters and the table rows come from the wiki page itself, as we will see later. When you want to view the page, the wiki has to determine the table structure solely from this Java class. It does this by reading the debug information of the class. For example, the names of the columns are generated from the names of the parameters of "tableRow(...)". So, when you refactor this class and reload the wiki page, the page will reflect your changes.

AutomaticReorderBasedOnReorderLimit

public class AutomaticReorderBasedOnReorderLimit extends WikiPage {
    public AutomaticReorderBasedOnReorderLimit() {
        addText("When the *items in stock* fall below the reorder limit " +
            "because of a sale, a reorder should be placed automatically.");
        addTabularExample(ExamplesOfAutoReordersBasedOnItemsInStock.class);
        getTabularExample(ExamplesOfAutoReordersBasedOnItemsInStock.class)
            .initialize(12);
        addText("");
    }

    @Test @TableInitialization 
            public void examplesOfAutoReordersBasedOnItemsInStock_row1() {
        runTableRow(0, getTabularExample(
            ExamplesOfAutoReordersBasedOnItemsInStock.class)
            .tableRow(12, 1, false));
    }
    @Test @TableInitialization 
            public void examplesOfAutoReordersBasedOnItemsInStock_row2() {
        runTableRow(10, getTabularExample(
            ExamplesOfAutoReordersBasedOnItemsInStock.class)
            .tableRow(12, 3, true));
    }
    @Test @TableInitialization 
            public void examplesOfAutoReordersBasedOnItemsInStock_row3() {
        runTableRow(20, getTabularExample(
            ExamplesOfAutoReordersBasedOnItemsInStock.class)
            .tableRow(12, 2, true));
    }
}

The constructor initializes all parts of the page. The table rows are initialized in specialized methods, one table row per method. The “row1”, “row2”, … suffixes of these methods are replaced with the row description if you entered it. These methods are annotated with the JUnit “Test” annotation, so you can already run this wiki page as a JUnit test. This is useful for debugging tests in your IDE. You can also run the test within the wiki, which will present the results as part of the page (similar to FitNesse).

Conclustions

Not everything I described in this post works yet (as of 2012-12-21), but I am still very satisfied with my progress. I think a tool like this would have several advantages over some existing tools. Integrating the executable specifications in your project is really easy with this approach. Also, you do not need any IDE plugins or DSL interpreters to run the examples. Implementing such a tool is a bit tricky, because the tool has to figure out all the data from the debug information in the Java class files. But this is something that can be done, and I hope that I can present some real code with my next blog post.

What do you think about this approach? If you don’t like it, which existing tool do you prefer and why? Please contact me (contact info is at the bottom of this page) and tell me your opinion!