Skip to content

Primer 0.2

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

NOTE: Release 0.2 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 CppSpecCalculatorSteps.cpp contains the C++ step definitions (written using CppSpec, but samples for Boost and GTest are provided as well). It starts by including the test framework to be used and the Cucumber connector (in this exact order):

#include <CppSpec/CppSpec.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:

struct 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("^I have entered (\\d+) into the calculator$") {
    REGEX_PARAM(double, n);
    USING_CONTEXT(CalcCtx, context);
    context->calc.push(n);
}

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

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

THEN("^the result should be (.*) on the screen$") {
    REGEX_PARAM(double, expected);
    USING_CONTEXT(CalcCtx, context);
    specify(context->result, should.equal(expected));
}

Steps are defined by one of these macros:

GIVEN("regular expression") { ... }
WHEN("regular expression") { ... }
THEN("regular expression") { ... }

Regular expression parameters can be accessed in the step body using the REGEX_PARAM macro. It takes two parameters: the variable type and name. 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…

void theCalculatorCanComputeBasicDivisions() {
  context->calc.push(3);
  context->calc.push(2);
  context->result = context->calc.divide();
  specify(context->result, should.equal(1.5));
}

Compiling and Running

The current library relies on a few libraries:

  • Boost 1.40+ (headers for smart_ptr and spirit, as well as compiled libraries for thread, system, regex, and date_time) – Should work with some previous versions too, but it wasn’t tested
  • GTest 1.4+ – Optional for the software test suite and the GTest driver. It wasn’t tested with GTest 1.3
  • CppSpec development branch – Optional for the CppSpec driver

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. Similarly you can define BOOST_ROOT and CPPSPEC_ROOT for Boost and CppSpec respectively.

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/CppSpecCalculatorSteps >/dev/null &
cucumber examples/Calc/CalcFeatures

To run the example on Windows (NMake):

start build\examples\Calc\CppSpecCalculatorSteps.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 # CppSpecCalculatorSteps.cpp:11
    And I have entered <input_2> into the calculator   # CppSpecCalculatorSteps.cpp:11
    When I press <button>                              # CppSpecCalculatorSteps.cpp:17
    Then the result should be <output> on the screen   # CppSpecCalculatorSteps.cpp:27

    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  # CppSpecCalculatorSteps.cpp:11
    And I have entered 2 into the calculator    # CppSpecCalculatorSteps.cpp:11
    When I press divide                         # CppSpecCalculatorSteps.cpp:22
    Then the result should be 1.5 on the screen # CppSpecCalculatorSteps.cpp:27

4 scenarios (4 passed)
16 steps (16 passed)
0m0.045s
Clone this wiki locally