Nahuel Garbezza

GNU/Linux, programming and more

Getting started with Kiwi

Hi, through this simple tutorial I’ll show you the basics of Kiwi with a practical example.

What is Kiwi?

Forget the delicious fruit for a while :-(. Kiwi is a Behaviour-Driven Development (BDD) tool. If you have used Cucumber (Ruby) you’ll find many things that look familiar, because Kiwi is inspired on Cucumber and it has the same specification syntax (Gherkin).

The project is on SqueakSource 3 and GitHub (for issue tracking).

If you don’t know about BDD, or you want to learn more, here are some links that may be useful for you:

One important thing about BDD is that specifications should be human-readable so all project members can understand them clearly. And they should be executable so they can be automated like a unit test.

Installation

Take a Pharo 1.3 image (It may work on other versions, but I can not assure that) and evaluate the following:

1
2
3
4
5
6
Gofer new
    url: 'http://ss3.gemstone.com/ss/Kiwi';
    package: 'ConfigurationOfKiwi';
    load.
 
(Smalltalk at: #ConfigurationOfKiwi) loadDevelopment.

I suggest to load the #development version, because there is no stable version yet.

About the example

I’m going to make a disclaimer here. The example I’ve chosen is very simple and it is not a good example for BDD, but I think it is a good example to show how Kiwi works, which BTW is the goal of this post. We’re going to implement a simple football matches logic. I promise to write a post with a more powerful example in the future.

How to start

You can use Kiwi through a GUI (which is a bit rudimentary yet), or you can work on a workspace. I will follow the UI approach but in a next post I will explain the workspace approach. To launch the Kiwi UI you can go to the World Menu -> Kiwi BDD or evaluate KiwiUI open in a workspace.

Because of the shortness of this example, we will do only one BDD iteration. But in a “real world” project there are a lot of iterations. So, let’s write our first specification. We’re going to use Gherkin syntax, but don’t worry, I will explain it (actually there aren’t so much to explain, it’s very simple) through the post. The spec I’m going to use is the following:

1
2
3
4
5
6
7
8
Feature: Round matches
 
  Scenario: Both draws are a draw
    Given a round match between "Team A" and "Team B"
    When the first leg finishes 1-1
      And the second leg finishes 2-2
    Then the global score should be 3-3
      And the result should be a draw

Before continuing with Kiwi I will explain a bit this syntax.

Gherkin syntax: features, scenarios and steps

Features, scenarios and steps are the basic elements of Gherkin syntax. There are more elements, but I’ll try to explain them in next posts. A specification in Gherkin starts with the Feature: keyword. In the same line goes an optional title, and in the lines below goes an optional description.

A feature has scenarios. These are concrete interactions between the user and the application. In the example above, Both draws are a draw. Scenarios start with the Scenario: keyword.

A scenario has steps. Steps are useful to describe in detail the scenario. There are three kind of steps:

  • initial context: these steps starts with the keyword Given and they are responsible for setting the initial context of the scenario.
  • actions: these steps starting with keyword When represent actions performed by the application.
  • expected outcomes: in these steps (starting with the keyword Then) we can check if the results generated by the previous action/s are the expected.

Steps starting with keywords And or But can be combined with any kind of step for more readability.

Back to Kiwi

Once you’ve opened the Kiwi UI, click the “Add feature” button, then select “New” from the popup window, and OK. In the text editor appeared, paste the content of the specification I’ve shown before.

The next step is to run the spec. We haven’t wrote any code yet, so we expect the specification doesn’t pass. As TDD, BDD follows the test-first practice :-)

So, save the feature with Ctrl+s and click the “Run current feature” button. You must see an output like the following:

Kiwi's result window example

It says “1 feature, 1 scenario and 5 steps”, all of them in undefined status. It means that Kiwi doesn’t now how to get to the code from the steps. So, we have to write step definitions.

Step definitions are code that responds to a step (or steps). In Kiwi, step definitions are represented as methods (if you have used Cucumber, SpecFlow or any other tool based on Gherkin, it’s almost the same thing). These methods use pragmas for storing the information to match steps. There are one pragma for each kind of step:

  • <given: regexp>
  • <when: regexp>
  • <then: regexp>

The regexp argument is a String used as a regular expression. When Kiwi runs, it searches for step definitions for each step in each scenario. If a method with a pragma whose regular expression match the step contents, this method is executed. Otherwise, the step is marked as undefined. When a step has a matching definition, it can be considered:

  • passed if the matching method executes without errors
  • failed if there was errors while performing the matching method
  • skipped if a previous step in the same scenario doesn’t pass (so it doesn’t make sense to continue executing the scenario)
  • pending if the method is explicitly tagged with the <pending> pragma.

So, let’s write step definitions for all the undefined steps. Create a class, name it as you want (I will call it FootballMatchesSteps), and add the following method:

1
2
3
givenARoundMatchBetween: teamA and: teamB
  <given: 'a round match between "([^"])*" and "([^"])*"'>
  <pending>

The regular expression ‘a round match between “([^"])*” and “([^"])*”‘ will match the step ‘a round match between “Team A” and “Team B”‘, and Kiwi will extract “Team A” and “Team B” and it will pass them as arguments to the #givenARoundMatchBetween: and: method. There must be a correspondence between regular expression and method signature, both must have the same number of arguments (otherwise we’ll get an error). This mechanism of extracting parameters of the regular expressions allows us to map one step definition to many steps. If we would have to write one step definition for each step, it would be very annoying.

Before running the feature again, we have to tell Kiwi where to look for step definitions. In the UI, click the “Add class” in the step definitions section, and then select the class you’ve just created (FootbalMatchesSteps in this example). Run the feature and you should see 1 step pending and 4 steps undefined. The results window has a tree widget in which you can see in detail the features, scenarios, steps, matching step definitions, etc.

Ok, we can continue creating step definitions for the rest of undefined steps. Then, remove the <pending> pragma and write the corresponding code. The 5 complete step definitions are defined as follows:

1
2
3
4
givenARoundMatchBetween: teamA and: teamB
  <given: 'a round match between "([^"])*" and "([^"])*"'>
 
  match := RoundMatch between: teamA and: teamB.
1
2
3
4
5
whenTheFirstLegFinishes: scoreA and: scoreB
  <when: 'the first leg finishes (\d)+-(\d)+'>
 
  "Kiwi pass all arguments as strings"
  match firstLegResult: scoreA asInteger to: scoreB asInteger.
1
2
3
4
whenTheSecondLegFinishes: scoreA and: scoreB
  <when: 'the second leg finishes (\d)+-(\d)+'>
 
  match secondLegResult: scoreA asInteger to: scoreB asInteger.
1
2
3
4
thenTheGlobalResultShouldBe: scoreA to: scoreB
  <then: 'the global score should be (\d)+-(\d)+'>
 
  match globalScore should equal: scoreA, '-', scoreB.
1
2
3
4
thenTheResultShouldBeADraw
  <then: 'the result should be a draw'>
 
  match isDraw should be: true.

In the two last step definitions I showed before, I used Mocketry for writing the “expectations”. It has a nice syntax for writing expressions like n should equal: 4 or list isEmpty should be: true, for instance. Here is a blog post explaining how to install & use Mocketry, I recommend it for writing the Then part of the step definitions. Otherwise, you can raise exceptions by hand, but I think it is a little ugly.

An important thing I didn’t mention before… if you want to share some state between steps (it is highly probably) you can set instance variables. All state remain while executing a scenario, an then it’s cleaned. In the example I’ve used match as instance variable.

Run the feature again, and you will see 1 failed step, and 4 skipped. You can use the UI to see what is the error about. As we can guess, it’s MessageNotUnderstood: RoundMatch class>>between:and:. Now it’s time to implement all the logic necessary to get the steps, the scenario and the feature passing.

Here are the methods I’ve implemented to make the feature pass:

1
2
3
RoundMatch class>>between: teamA and: teamB
 
    ^self new teamA: teamA; teamB: teamB; yourself
1
2
3
4
RoundMatch>>firstLegResult: scoreA to: scoreB
 
    teamAFirstLegScore := scoreA.
    teamBFirstLegScore := scoreB.
1
2
3
4
RoundMatch>>secondLegResult: scoreA to: scoreB
 
    teamASecondLegScore := scoreA.
    teamBSecondLegScore := scoreB.
1
2
3
RoundMatch>>globalScoreTeamA
 
    ^teamAFirstLegScore + teamASecondLegScore.
1
2
3
RoundMatch>>globalScoreTeamB
 
    ^teamBFirstLegScore + teamBSecondLegScore.
1
2
3
RoundMatch>>globalScore
 
    ^self globalScoreTeamA printString , '-' , self globalScoreTeamB printString
1
2
3
RoundMatch>>isDraw
 
    ^self globalScoreTeamA = self globalScoreTeamB

If you get all the steps passing, it means the scenario has completed and a BDD cycle has finished :D. Of course, in this example’s feature there are many scenarios left to implement, to cover all possible results (winning a match, winning both matches, etc).

You can load the code of this example by evaluating the following:

1
2
3
4
5
6
7
Gofer new
    url: 'http://ss3.gemstone.com/ss/Kiwi';
    package: 'ConfigurationOfKiwi';
    load.
 
((Smalltalk at: #ConfigurationOfKiwi) project version: #development)
    load: 'Kiwi-Examples-FootballMatches'.

And you can open this example with the UI by evaluating:

1
KiwiUI openOn: (FootballMatchesProfiles profileAt: 'basic')

I’ll cover more scenarios and Kiwi features in a next post. But I think I’ll give you the basics to continue playing with Kiwi, if not (or if something isn’t clear at all), please tell me, and I try to improve the post. Stay tuned!

, , ,

6 thoughts on “Getting started with Kiwi

  • Laurent says:

    Do we have to maintain method names and almost identical regexes ? In most cases it seems the method name is sufficient to link step definitions and methods. Some DRY would be good here.

    Anyway, nice work. Good to see Smalltalk catches up.

    • nahuel says:

      Hi Laurent, I’ve followed the approach used in many other Cucumber implementations (a method with arbitrary name, and metadata for the step definition information). But it is true, there is some duplication… I will search Cucumber mailing lists, this probably has already been discussed.

      As an alternative, I’ve thought of a syntax like this:

      someObject given: 'regexp' do: [ "your step definition code" ].

      but Smalltalk forces me to put these snippets in a method.

      Thanks for commenting!

      • Laurent says:

        At least there can be some IDE goodies: open an editor on the bottom when clicking on the step definition in the Results window ?

        • nahuel says:

          What I did (because I don’t know much about tree morph, events and context menus) is to place a button that opens a browser with the step definition selected.

          Anyway, I’m considering reimplementing the UI using Glamour.

  • Tim Felgentreff says:

    Did you look at AcceptIt? Do you compile the features into methods, too, or is that just a string you keep somewhere? AcceptIt uses OMeta to compile features into executable code directly which I find is a very elegant solution.

    http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/acceptit.html

    • nahuel says:

      Thanks Tim, I will take a look on AcceptIt. Yes, the features can be stored as methods (currently as methods returning a string with the feature content).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>