(Quick Reference)

5 Tutorials - Reference Documentation

Authors: Erik Pragt (jWorks.nl), Marcin Erdmann, John Engelman

Version: 2.0.3

5 Tutorials

5.1 Integration testing

This tutorial describes the creation of a simple application and how to test it.

1. Create your Grails application.

$ grails create-app bookstore
$ cd bookstore

2. Install the plugin.

$ grails install-plugin fitnesse

We are going to create a small bookstore with a small domain to test the core functionality without using a Functional or Unit test. Normally, I would recommend using Unit tests after writing the Fitnesse Acceptance Test, but that is outside the scope of this tutorial.

To start in a Test Driven approach, we'll start by creating the test first.

To do so, we'll need to start Fitnesse as well as Grails.

3. Start the Grails server.

$ grails run-app

4. Start the Fitnesse server.

Since Fitnesse is bundled with the plugin, there's no need to download anything.

The following will extract Fitnesse and start it on the default port, 9090.

$ grails run-fitnesse

5. Create the test

Open a web browser and point it to http://localhost:9090 . The Fitnesse front page will show up.

Click on Edit, and add the following to the text area: ^BookStoreTest . Press Save.

You will now have a text with a question mark next to it. Click on the question mark to create a new page.

In the new page, remove the contents of the text area and replace it by the following:

!define TEST_SYSTEM {slim}
!define COMMAND_PATTERN {echo}

|import| |bookstore|

|create book inventory| |author|title|amount| |Stephen King|IT|3| |Dean Koontz|Chase|5|

|script|buy book scenario| |customer buys|2|books with title|IT| |customer buys|1|books with title|Chase|

|query:check book inventory| |author|title|amount| |Stephen King|IT|1| |Dean Koontz|Chase|4|

Press save after you created the page.

The COMMAND_PATTERN should (currently) have a dummy value! I have chosen 'echo' since I'm on a Mac, but for Windows users you need to create a batch file, for example, echo.bat, without any contents. The reason for doing this is that Fitnesse normally starts a Slim Server itself. However, in our case, our Grails application starts the Slim Server for us. So, to prevent starting the server, we use a dummy command pattern.

In this test, you've used 3 kinds of Test tables:

  • a Decision Table (create book inventory) used to setup the data
  • a Script Table (buy book scenario) to execute the test
  • a Query table (check book inventory) to verify the results of the test

Click on Test on the left side of the page. The test will execute, and will fail with errors like Could not invoke constructor for CreateBookInventory[0]. This is because no Fixtures have been created yet. This way of working is in line with the 'Red-Green-Refactor' TDD mantra; our test clearly is red (or yellow, in this case), so we need to focus on getting the test green. To make the test pass, we need to create the fixtures.

6. Creating the Fixtures

We'll start by creating the first Fixture mentioned in the error message, which is the CreateBookInventory Fixture. To create the Fixture, use the Grails task @create-fitnesse-fixture. You can involve the script by typing the following:

$ grails create-fitnesse-fixture CreateBookInventory

This will create a file called @CreateBookInventoryFixture.groovy in the grails-app/fitnesse/bookstore@ directory.

Because the Fitnesse plugin will reload the Fixtures automatically, pressing the Test* button in the Wiki will already show that the Fixture has been loaded correctly, and the cell is colored green by Fitnesse. If for some reason this doesn't work, please restart the Grails application, and try again.

Now, we'll have to create the author, title and amount properties in the Fixture and the corresponding Book domain class.

Note, Grails might crash due to the Fixture missing the domain classes. Whenever this happens, please restart Grails.

6.1 The Decision Table

The Fixture to support the Decision Table should look like this:

class CreateBookInventoryFixture {
    String author
    String title

int amount

def bookService

CreateBookInventoryFixture() { Book.list()*.delete() }

void execute() { amount.times { bookService.addBook(new Book(author: author, title: title)) } } }

The Fixture has an execute method. This method is called after input parameters have been set, but before any output parameters are requested. This allows us to have a place to do something with the input, and prepare the output.

The Book class like this:

class Book {
    String author
    String title
}

We also need a BookService class, since the Fixtures should not hold any logic; they are just a translation of the test with the SUT. The BookService looks like this:

class BookService {

static transactional = true

def addBook(book) { book.save() }

def buyBook(title) { Book.findByTitle(title).delete() }

def checkInventory() { Book.executeQuery("select b.title, b.author, count(*) from Book b group by title, author") } }

Execute the test again, and the first part of the test should be green. If not, and you're sure you've executed all steps correctly, please let me know because it would mean there's an error in the tutorial, or I've been unclear in the way to explain things. In any way, if you're stuck, please let me know.

6.2 The Script Table

The Script Fixture to execute the Script Table should look like this:

class BuyBookScenarioFixture {
    def bookService

void customerBuysBooksWithTitle(int amount, title) { amount.times { bookService.buyBook(title) } } }

Note that this implementation is very simplistic. In this example, I simply delete the books from the inventory instead of assigning them to a customer or a basket.

6.3 The Query Table

Finally, to verify if our assertions match the execution of our code, we need to check the results of our actions. We do this by using a Query Table.

class CheckBookInventoryFixture {
    static queryFixture = true  // indication that this is a query fixture
    static mapping = [title: 0, author: 1, amount: 2]  // the mapping

def bookService // injected service

def queryResults() { // queryResults() method, which must be named like this! bookService.checkInventory() } }

The mapping is used here to map the column names to the position of the values returned by the queryResults method. For more information, please check the 'Query DSL' part in the 'Features' documentation.

Now, when running the Test again in Fitnesse, everything should be green, and you have your first test running. Not you can refactor your code if you want, and check if the result is still valid by rerunning the Tests!

5.2 Refcard

I (Erik Pragt) have written a Refcard to explain the basics of Fitnesse. It provides a starting point for those unfamiliar with the basics of Fitnesse and also provides some more advanced tips for those who have worked with Fitnesse before.

It has been published by DZone, and you can read it here.

5.3 Example project

To properly test our plugin, we have creates an example project which goes through all the features the plugin provides, like exception handling, transactions, conversions, etc.

The example project is not bundled in the plugin itself, but can be found as a separate download, which can be found on github.