Cucumber Tcl
Drive your Tcl code with Cucumber.
Dependencies
You'll need the following:
- tcl8.5 and dev libraries
- ruby > 3.0.0 along with its dev libraries
How to use
First, add the cucumber-tcl plugin to your Gemfile:
gem 'cucumber-tcl'
Install it:
bundle install
In a file in Cucumber's load path, like features/support/env.rb, add the following line:
require 'cucumber/tcl'
You should now be able to start writing features, and implementing the step definitions in Tcl. These should be placed in .tcl files below the features directory. To create the step definitions, you're provided with Given, When, Then and And procs for you to call, for example:
Given {^I am a logged in user$} {
puts "I'm a logged in user
}
When {^I purchase a ticket$} {
puts "Purchase a ticket"
}
Then {^I receive confirmation of the purchase$} {
puts "Purchase confirmation"
}
If your regular expression captures any matches, you should provide a list of variable names as the second parameter to any of these procedure calls. These will then be available to your script, for example:
Given {^I am logged in with username (\w+)$} {username} {
puts "Username is $username"
}
Given {^I buy (\d+) cucumbers for $(\d+)$} {quantity price} {
puts "$quantity cucumbers bought. Price was $price"
}
You can use basic tables in your scenarios. The data from the table s made available to your step definition, via the last variable name you pass into the capture list. For example, if you had the following step in your feature:
Given I have added the following products to my shopping cart:
| apple | £2.00 |
| orange | £3.00 |
| banana | £1.50 |
I could write the step definition for the Given step as:
Given {^I have added the following products to my shopping cart:$} {table_data} {
puts "$table_data"
}
The data in the table is provided as a list of lists, so in the above example, table data would look like:
{"apple" "£2.00"} {"orange" "£3.00"} {"banana" "£1.50"}
meaning you can access each element using lindex, e.g.
puts [lindex [lindex $table_data 0] 0]
apple
If your step definition captures a match in the step definition as well as has a table, the table variable will always be last, e.g.
When I buy the following items from Tesco:
| cabbage |
| potato |
| onion |
and in your step definition, you might have
Given {^I buy the following items from (\w+):$} {store items} {
puts "I've been shopping at $store"
puts "The first item I bought was [lindex $items 0]"
}
Resetting state between scenarios
Depending on how your test and/or application code is structured, there may be a chance of data persisting between scenarios, which could result in tests that pass or fail unexpectedly. To eliminate the risk of this, Cucumber TCL will start a new TCL interpreter between every scenario by creating a new instance of the 'framework' object, meaning that the env.tcl file is loaded each time. Whilst this will remove the data leakage risk, it may also cause your scenarios to run slowly if there is a lot of setup required for a scenario to run (eg, setting up fixture data, building a database or loading large amounts of data into memory). To override the default behaviour of starting up a new interpreter, an environment variable can be passed into the 'cucumber' command enabling the sharing of the TCL interpreter via the 'framework' object:
cucumber SHARE_FRAMEWORK=1
It's also possible to make the default behaviour of starting up a new interpreter explicit:
cucumber SHARE_FRAMEWORK=0
When wrapping Cucumber around legacy Tcl code, you can speed up local development by sharing the framework object to avoid starting a new Tcl interpreter for every test. However, you should keep the default behavior in your CI build to prevent potential data leakage between tests.
Returning Test Step Results
Cucumber knows different types of results defined in:
lib/cucumber/core/test/result.rb
Passed, Failed are the most common test results, while Skipped, Pending and Undefined are "exceptions that can be raised in a step definition causing the step to have that result."
To make a test step "Passed", use:
return -code ok
in your tcl implementation. (tcl automatically returns that at the end of each step)
To make a test step "Failed", raise an error:
error "The shopping cart is empty"
To trigger the exceptions "Skipped", "Pending" or "Undefined", use these keywords as error message.
error "skipped"; # this test step shall appeared as "Skipped" (=blue in reports)
error "pending; # this test step shall appeared as "Pending" (= unimplemented)
error "undefined"; # this test step shall appeared as "Undefined"