4.7. Step - 05 : Make tests fail

Now, let’s add a very basic libMath library, and make tests fail.

The updated directory structure would be like this. (Bear with me, this is no Python. It’s C/C++. Just to add an empty file with missing implementation, we will need to create 3 files, and update at-least 1-3 more file):

.
|-- features
|   |-- step_definitions
|   |   |-- cucumber.wire
|   |   `-- steps_addition.cpp
|   |-- CMakeLists.txt
|   `-- simple-addition.feature
|-- lib
|   |-- CMakeLists.txt
|   |-- libMath.c
|   `-- libMath.h
`-- CMakeLists.txt
  • lib/libMath.h

    This new header file has the interface for the libMath APIs.

    
    #ifdef __cplusplus
    extern "C" {
    #endif // __cplusplus
    
    int libMath_add(int param1, int param2);
    
    #ifdef __cplusplus
    } // extern "C"
    #endif // __cplusplus
    
  • lib/libMath.c

    As of now, the file lib/libMath.c will not have any implementation. (Remember we still want to fail tests.)

    
    #include <libMath.h>
    
    int libMath_add(int param1, int param2) {
        return -1; // FIXME
    }
    
  • lib/CMakeLists.txt

    A minimal CMakeLists.txt file is needed to make this as a library.

    PROJECT(libMath)
    
    ADD_LIBRARY(
        ${PROJECT_NAME}
        libMath.c
        libMath.h
    )
    
    TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC .)
    
  • features/CMakeLists.txt

    Update the Feature executable CMake File so that it can link to the new libMath.

    PROJECT(features)
    
    FILE(
        GLOB
        all_files
        step_definitions/*.cpp
        step_definitions/*.hpp
        *.feature
    )
    
    ADD_EXECUTABLE(${PROJECT_NAME} ${all_files})
    
    TARGET_LINK_LIBRARIES(
        ${PROJECT_NAME}
        CucumberCpp::cucumber-cpp
        libMath
    )
    
  • <TOP>CMakeLists.txt

    Update the top level <TOP>CMakeLists.txt to also .

    CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
    
    PROJECT(DemoBDD)
    
    INCLUDE(/usr/local/lib/cmake/CucumberCppConfig.cmake)
    
    SET(BOOST_MIN_VERSION "1.51")
    
    SET(CUKE_CORE_BOOST_LIBS
        thread
        system
        regex
        date_time
        program_options
        filesystem
    )
    FIND_PACKAGE(
        Boost
        ${BOOST_MIN_VERSION}
        COMPONENTS ${CUKE_CORE_BOOST_LIBS}
        REQUIRED
    )
    
    FIND_PACKAGE(
        GMock
        REQUIRED
    )
    FIND_PACKAGE(
        GTest
        REQUIRED
    )
    
    ADD_SUBDIRECTORY(features)
    ADD_SUBDIRECTORY(lib)
    

4.7.1. Changes

As compared to previous step (Step - 04 : Add Dummy Steps), the change is long, and hence listed at the end of this section at Changes - full diff.

4.7.2. Use CMake

Using CMake, we configure/create a work space, because we have added a new library libMath:

cmake -S . -B ../_05-failing-tests

If everything is setup perfectly, the output would be similar to Step - 01 : Setup of C/C++ Code. (Not shown here)

4.7.3. Compile the math library and feature executable

Let’s compile the workspace:

make all

If everything is setup perfectly, the output looks like this:

[  8%] Creating directories for 'GMock'
[ 16%] No download step for 'GMock'
[ 25%] No update step for 'GMock'
[ 33%] No patch step for 'GMock'
[ 41%] Performing configure step for 'GMock'
-- The CXX compiler identification is GNU 11.3.0
-- The C compiler identification is GNU 11.3.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Found Python: /home/c6h6/.virtualenvs/ratuenv/bin/python3 (found version "3.10.6") found components: Interpreter 
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Configuring done
-- Generating done
-- Build files have been written to: /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/_05-failing-tests/gmock
[ 50%] Performing build step for 'GMock'
[ 12%] Building CXX object /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/_05-failing-tests/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 25%] Linking CXX static library /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/_05-failing-tests/gmock/lib/libgtest.a
[ 25%] Built target gtest
[ 37%] Building CXX object CMakeFiles/gmock.dir/src/gmock-all.cc.o
[ 50%] Linking CXX static library lib/libgmock.a
[ 50%] Built target gmock
[ 62%] Building CXX object CMakeFiles/gmock_main.dir/src/gmock_main.cc.o
[ 75%] Linking CXX static library lib/libgmock_main.a
[ 75%] Built target gmock_main
[ 87%] Building CXX object /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/_05-failing-tests/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[100%] Linking CXX static library /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/_05-failing-tests/gmock/lib/libgtest_main.a
[100%] Built target gtest_main
[ 58%] No install step for 'GMock'
[ 66%] Completed 'GMock'
[ 66%] Built target GMock
[ 75%] Building C object lib/CMakeFiles/libMath.dir/libMath.c.o
[ 83%] Linking C static library liblibMath.a
[ 83%] Built target libMath
[ 91%] Building CXX object features/CMakeFiles/features.dir/step_definitions/steps_addition.cpp.o
[100%] Linking CXX executable features
[100%] Built target features

And, we must have a feature executable generated, as well as libMath

4.7.4. Running feature executable

As a first step, we need to run the feature executable in background.

./features/features -v &

If everything is setup perfectly, this process will wait for Cucumber. And once we run Cucumber, the full output would be like this:

Listening on 127.0.0.1:3902
/home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/05-failing-tests/features/step_definitions/steps_addition.cpp:39: Failure
Expected equality of these values:
  4
  context->result
    Which is: -1
/home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/05-failing-tests/features/step_definitions/steps_addition.cpp:44: Failure
Expected equality of these values:
  99
  context->result
    Which is: -1

4.7.5. Running cucumber

Now let’s run Cucumber.

cucumber

The output is as follows.

*** THIS RUBY IMPLEMENTATION DOESN'T REPORT FILE AND LINE FOR PROCS ***
# features/sample-addition.feature
Feature: Simple Addition
  
    Showcase simple addition for the BDD Book.

  Scenario: Addition of single digit numbers # features/simple-addition.feature:7
    Given I have '1' and '3'                 # steps_addition.cpp:20
    When I add them                          # steps_addition.cpp:32
    Then The result must be '4'              # steps_addition.cpp:37
      /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/05-failing-tests/features/step_definitions/steps_addition.cpp:39: Failure
      Expected equality of these values:
        4
        context->result
          Which is: -1 (Cucumber::WireSupport::WireException)
      features/simple-addition.feature:10:in `Then The result must be '4''

  Scenario: Addition of double digit numbers # features/simple-addition.feature:12
    Given I have '70' and '29'               # steps_addition.cpp:26
    When I add them                          # steps_addition.cpp:32
    Then The result must be '99'             # steps_addition.cpp:42
      /home/c6h6/data/p/book-tests/pgh_tech-bdd/book/csrc/05-failing-tests/features/step_definitions/steps_addition.cpp:44: Failure
      Expected equality of these values:
        99
        context->result
          Which is: -1 (Cucumber::WireSupport::WireException)
      features/simple-addition.feature:15:in `Then The result must be '99''

Failing Scenarios:
cucumber features/simple-addition.feature:7 # Scenario: Addition of single digit numbers
cucumber features/simple-addition.feature:12 # Scenario: Addition of double digit numbers

2 scenarios (2 failed)
6 steps (2 failed, 4 passed)
0m0.022s

As you can see, the output is different from Step - 04 : Add Dummy Steps. We now see failing tests.

4.7.6. Changes - full diff

As compared to previous step (Step - 04 : Add Dummy Steps), the diff is:

diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/CMakeLists.txt new/CMakeLists.txt
--- old/CMakeLists.txt	2022-12-11 00:00:00.000000000 +0530
+++ new/CMakeLists.txt	2022-12-11 00:00:00.000000000 +0530
@@ -31,3 +31,4 @@ FIND_PACKAGE(
 )
 
 ADD_SUBDIRECTORY(features)
+ADD_SUBDIRECTORY(lib)
diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/features/CMakeLists.txt new/features/CMakeLists.txt
--- old/features/CMakeLists.txt	2022-12-11 00:00:00.000000000 +0530
+++ new/features/CMakeLists.txt	2022-12-11 00:00:00.000000000 +0530
@@ -13,4 +13,5 @@ ADD_EXECUTABLE(${PROJECT_NAME} ${all_fil
 TARGET_LINK_LIBRARIES(
     ${PROJECT_NAME}
     CucumberCpp::cucumber-cpp
+    libMath
 )
Files old/features/simple-addition.feature and new/features/simple-addition.feature are identical
Files old/features/step_definitions/cucumber.wire and new/features/step_definitions/cucumber.wire are identical
diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/features/step_definitions/steps_addition.cpp new/features/step_definitions/steps_addition.cpp
--- old/features/step_definitions/steps_addition.cpp	2022-12-11 00:00:00.000000000 +0530
+++ new/features/step_definitions/steps_addition.cpp	2022-12-11 00:00:00.000000000 +0530
@@ -2,26 +2,44 @@
 #include <gtest/gtest.h>
 #include <cucumber-cpp/autodetect.hpp>
 
+#include <libMath.h>
+
+using cucumber::ScenarioScope;
+
+struct libMathCtx {
+    int param1;
+    int param2;
+    int result;
+};
+
+
 // We could have used Regular Expressions here
 // to reduce the duplicated steps.
 // but we will introduce that concept later.
 
 GIVEN("^I have '1' and '3'$") {
-    pending();
+    ScenarioScope<libMathCtx> context;
+    context->param1 = 1;
+    context->param2 = 3;
 }
 
 GIVEN("^I have '70' and '29'$") {
-    pending();
+    ScenarioScope<libMathCtx> context;
+    context->param1 = 70;
+    context->param2 = 29;
 }
 
 WHEN("^I add them$") {
-    pending();
+    ScenarioScope<libMathCtx> context;
+    context->result = libMath_add(context->param1, context->param2);
 }
 
 THEN("^The result must be '4'$") {
-    pending();
+    ScenarioScope<libMathCtx> context;
+    EXPECT_EQ(4, context->result);
 }
 
 THEN("^The result must be '99'$") {
-    pending();
+    ScenarioScope<libMathCtx> context;
+    EXPECT_EQ(99, context->result);
 }
diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/lib/CMakeLists.txt new/lib/CMakeLists.txt
--- old/lib/CMakeLists.txt	1970-01-01 05:30:00.000000000 +0530
+++ new/lib/CMakeLists.txt	2022-12-11 00:00:00.000000000 +0530
@@ -0,0 +1,9 @@
+PROJECT(libMath)
+
+ADD_LIBRARY(
+    ${PROJECT_NAME}
+    libMath.c
+    libMath.h
+)
+
+TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC .)
diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/lib/libMath.c new/lib/libMath.c
--- old/lib/libMath.c	1970-01-01 05:30:00.000000000 +0530
+++ new/lib/libMath.c	2022-12-11 00:00:00.000000000 +0530
@@ -0,0 +1,6 @@
+
+#include <libMath.h>
+
+int libMath_add(int param1, int param2) {
+    return -1; // FIXME
+}
diff '--unified=3' --new-file --ignore-all-space --text --recursive --show-c-function --report-identical-files old/lib/libMath.h new/lib/libMath.h
--- old/lib/libMath.h	1970-01-01 05:30:00.000000000 +0530
+++ new/lib/libMath.h	2022-12-11 00:00:00.000000000 +0530
@@ -0,0 +1,10 @@
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+int libMath_add(int param1, int param2);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus