Skip to content

Primer 0.1

Paolo Ambrosio edited this page Aug 12, 2012 · 2 revisions

NOTE: Release 0.1 was named CukeBins

Defining C++ Steps

To use CukeBins, you have to write the standard Cucumber scenarios, set up the wire protocol connection and write the C++ step definitions.

Cucumber Setup

Nothing strange here. The project structure is the same as with Cuke4Nuke:

MyProject/features/
MyProject/features/some_feature.feature
MyProject/features/another_feature.feature
MyProject/features/step_definitions/
MyProject/features/step_definitions/cukebins.wire

The .wire file tells Cucumber that it should use the wire protocol. The file has two lines:

host: localhost
port: 3902

The default port for CukeBins server is 3902. If you need to change it, you should add the same port number as the first argument while running the steps executable.

Writing C++ Code

CukeBins includes a sample borrowed from Cuke4Nuke that uses the same feature definitions but tests a C++ implementation. Let’s analyze it.

The file CalculatorSteps.cpp contains the C++ step definitions. It starts by including the test framework to be used and the Cucumber connector (in this exact order):

#include <gtest/gtest.h>
#include <cukebins/wireserver.hpp>

Then it includes the code to be tested and defines an common area to be used by every step in a scenario, that we call Context:

CUKE_CONTEXT(CalcCtx) {
    Calculator calc;
    double result;
};

The contexts will be automatically created before and cleared after every scenario (actually they are created only when needed by a step). The step code will be able to access a “context” pointer of the defined type, as we will see. In the sample application the steps are defined as:

GIVEN(CalcCtx, EnterNumber, "^I have entered (\\d+) into the calculator$") {
    CUKE_PARAM(1, double, n);
    context->calc.push(n);
}

WHEN(CalcCtx, Add, "^I press add") {
    context->result = context->calc.add();
}

WHEN(CalcCtx, Divide, "^I press divide") {
    context->result = context->calc.divide();
}

THEN(CalcCtx, CheckResult, "^the result should be (.*) on the screen$") {
    CUKE_PARAM(1, double, expected);
    EXPECT_EQ(expected, context->result);
}

Steps are defined by one of these macros:

GIVEN(ContextName, StepName, "regular expression") { ... }
WHEN(ContextName, StepName, "regular expression") { ... }
THEN(ContextName, StepName, "regular expression") { ... }

Regular expression parameters can be accessed in the step body using the CUKE_PARAM macro. It takes three parameters: the parameter position in the regex (counting from 1!), the variable type and name to be created. The type can be every C++ type that can be extracted by a std::istringstream (so you can even define your own types implementing the extraction operator).

So executing the following scenario…

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

…would correspond to something like…

TEST(SomeTestName, SomeTestCaseName) {
  context->calc.push(3);
  context->calc.push(2);
  context->result = context->calc.divide();
  EXPECT_EQ(1.5, context->result);
}

Fixture Classes

A fixture for a step, can be defined with the macro CUKE_FIXTURE. Apart from that, the definition should be that of a normal GTest Fixture.

CUKE_FIXTURE(FixtureName, ContextName) {
  ...
  void SetUp() { ... }
  void TearDown() { ... }
}

To associate that fixture to a step you should then add the _F suffix to the step definition macros:

GIVEN_F(FixtureName, StepName, "regular expression") { ... }
WHEN_F(FixtureName, StepName, "regular expression") { ... }
THEN_F(FixtureName, StepName, "regular expression") { ... }

Compiling and Running

The current library is header-only, so it doesn’t need to be compiled separately, but it needs the following libraries when compiling the step definitions:

  • Boost 1.40 (thread, system, regex, and date_time libraries) – Should work with some previous versions too, but it wasn’t tested
  • GTest 1.4 – Needed by the test suite and the GTest implementation. Works with GTest 1.4 and 1.5 (not tested with 1.3)

This header-only library is included in the source code:

  • JSON Spirit – Needed by the Wire Protocol connector

CukeBins uses the CMake build system, so you also need CMake 2.8 to run the tests and the example project. This is how to complile them:

cmake -E make_directory build
cmake -E chdir build cmake ..
cmake --build build
cmake --build build --target test

If CMake can’t find the required libraries, you can define variables to specify the path where to look:

  • For GTest, set GTEST_ROOT to the location of the GTest install prefix. If the GTest libraries were installed in /usr/local/lib and the includes are in /usr/local/include/gtest, then you should add -DGTEST_ROOT=/usr/local to the cmake command line
  • For Boost, set BOOST_ROOT to the location of the Boost install prefix if CMake has troubles finding it

This is more complex example of CMake command line specifying both a Visual Studio Project generator and paths for the libraries:

cmake -E make_directory vs_project
cmake -E chdir vs_project cmake -G"Visual Studio 9 2008" -DGTEST_ROOT=X:\gtest-1.4.0 -DBOOST_ROOT=X:\boost_1_40_0 ..

This is an example of how to create an Eclipse Project:

cmake -E make_directory eclipse_project
cmake -E chdir eclipse_project cmake -G"Eclipse CDT4 - Unix Makefiles" -DGTEST_ROOT=/usr/local ..

Running the Example

To run the example on Unix:

build/examples/Calc/CalculatorSteps >/dev/null &
cucumber examples/Calc/CalcFeatures

To run the example on Windows (NMake):

start build\examples\Calc\CalculatorSteps.exe
cucumber examples\Calc\CalcFeatures

You should see Cucumber scenarios passing:

# language: en
Feature: Addition
  In order to avoid silly mistakes
  As a math idiot
  I want to be told the sum of two numbers

  Scenario Outline: Add two numbers                    # examples/Calc/CalcFeatures/features/addition.feature:7
    Given I have entered <input_1> into the calculator # Unknown
    And I have entered <input_2> into the calculator   # Unknown
    When I press <button>                              # Unknown
    Then the result should be <output> on the screen   # Unknown

    Examples: 
      | input_1 | input_2 | button | output |
      | 20      | 30      | add    | 50     |
      | 2       | 5       | add    | 7      |
      | 0       | 40      | add    | 40     |

# language: en
Feature: Division
  In order to avoid silly mistakes
  Cashiers must be able to calculate a fraction

  Scenario: Regular numbers                     # examples/Calc/CalcFeatures/features/division.feature:6
    Given I have entered 3 into the calculator  # Unknown
    And I have entered 2 into the calculator    # Unknown
    When I press divide                         # Unknown
    Then the result should be 1.5 on the screen # Unknown

4 scenarios (4 passed)
16 steps (16 passed)
Clone this wiki locally