Acceptance Test Driven Development has become a popular practice for establishing and validating what done means for a feature. Cucumber has gained much popularity in validating Ruby code, especially for Ruby on Rails web applications. The Wire Protocol was added to Cucumber to support testing different languages by implementing a simple Wire Server in the target language. The Wire support allows step definitions to be written in an arbitrary language with communication over a TCP socket.
The following example shows how this works with C++ using Cucumber-Cpp (thanks Paolo Ambrusio) and an example that comes bundled with it.
Features are written in Gherkin, just like standard Cucumber tests. Gherkin is a business-level DSL (Domain-Specific Language) that allows test to be written in high level terminology. This allows tests to be written and understood by non-developers, thus facilitating the conversion of what done should mean for a given feature. Below is a feature definition and scenario for a simple calculator application to validate division:
Feature: Division
In order to avoid silly mistakes
Cashiers must be able to calculate a fraction
Scenario: Regular numbers
Given I have entered 3 into the calculator
And I have entered 2 into the calculator
When I press divide
Then the result should be 1.5 on the screen
The implementation of the step definitions for all features and the corresponding support for setting up the test fixture are contained in a C++ file. Below is the source file that implements all the needed step definitions for the feature file above, though it also includes step definitions for the rest of the feature files in the test suite. At the top of the file is the implementation of the Context class that is instantiated prior to executing each feature scenario, and is destroyed when the scenario terminates. In the current implementation of Cucumber-Cpp, exceptions (including those in test assertion frameworks) cannot occur in the Context constructor nor destructor. There is a request to in to resolve this limitation.
Wire communication is done via a simple protocol encoded in JSON. Executing the feature file above yields the output below, which is the same output you would get from a true Cucumber test. Although, the C++ step definitions get annotated with the source file and line number for cross referencing.
Feature: Division
In order to avoid silly mistakes
Cashiers must be able to calculate a fraction
Scenario: Regular numbers # division.feature:6
Given I have entered 3 into the calculator # CalculatorSteps.cpp:11
And I have entered 2 into the calculator # CalculatorSteps.cpp:11
When I press divide # CalculatorSteps.cpp:22
Then the result should be 1.5 on the screen # CalculatorSteps.cpp:27
4 scenarios (4 passed)
16 steps (16 passed)
0m0.073s
Adding some debug output to Cucumber shows how the execution happens across The Wire:
Scenario: Regular numbers // division.feature:6
// Cucumber sends this message to the Wire server to start the scenario
> ["begin_scenario"]
// Each message sent to the Wire server is acknowledged,
// and may include response data as well
< ["success"]
// Cucumber sends the first step of the scenario
> ["step_matches",
{
"name_to_match":"I have entered 3 into the calculator"
}
]
// The Wire server responds with the regular expression that
// matches the specified step, including the extracted parameter
// and location of the step definition
< ["success",
[
{
"regexp":"^I have entered (\\d+) into the calculator$",
"args":[{"val":"3","pos":15}],
"id":"1",
"source":"CalculatorSteps.cpp:11"
}
]
]
// Cucumber now tells the server to go ahead and execute the step
> ["invoke",
{
"args":["3"],
"id":"1"
}
]
// The server responds with the result
< ["success"]
Given I have entered 3 into the calculator # CalculatorSteps.cpp:11
As you can see, the protocol and data exchange is pretty straight-forward. In addition to Cucumber-Cpp, there are Wire implementations for other languages as well.
Thanks a lot.
Developing a new C++ library, I just start defining its features using Cucumber as suggested in your article and bundle everything with CMake (GTest and Cucumber as ExternalProject).
It’s a complete win.
Bertrand