Modernes C++ programmieren

Okt 20, 2024

listings-Chap14-README-onepage.md

Listings of Chap14.docx

This is the list of listings on one page. You can also view a linked summary.

Listing 14.1: Solitary tests test only one component, and helper classes are substituted.

Book listing lst-0001-book.cpp:

// https://godbolt.org/z/a83sf9h8E 
#include <iostream>
// Production code:
struct DatabaseInterface {
    virtual int getData() const = 0;
};
struct Program {
    DatabaseInterface &db_;
    void run() {
        std:: cout << db_.getData() << "\n”;
    }
};
// Test helper:
struct MockDatabase : public DatabaseInterface {
    int getData() const override { return 5; }
};
// main as test:
int main() {
    MockDatabase mockDb;
    Program prog { mockDb }; // real DB is not tested
    prog.run();                // Expected output: 5
}

Godbolt Listing lst-0001-godb.cpp, https://godbolt.org/z/a83sf9h8E:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/a83sf9h8E 
#include <iostream>
// Production code:
struct DatabaseInterface {
    virtual int getData() const = 0;
};
struct Program {
    DatabaseInterface &db_;
    void run() {
        std:: cout << db_.getData() << "\n”;
    }
};
// Test helper:
struct MockDatabase : public DatabaseInterface {
    int getData() const override { return 5; }
};
// main as test:
int main() {
    MockDatabase mockDb;
    Program prog { mockDb }; // real DB is not tested
    prog.run();                // Expected output: 5
}

Listing 14.2: Social tests test the interaction of components.

Book listing lst-0002-book.cpp:

// https://godbolt.org/z/fsroE4seh 
#include <iostream>

// Production code:
struct DatabaseInterface {
    virtual int getData() const = 0;
};
struct RealDatabase : public DatabaseInterface {
    int getData() const override { return 999; }
};
struct Program {
    DatabaseInterface &db_;
    void run() {
        std:: cout << db_.getData() << "\n";
    }
};

// main as test:
int main() {
    RealDatabase db;
    Program prog { db }; // real DB is tested along
    prog.run();            // Expected output: 999
}

Godbolt Listing lst-0002-godb.cpp, https://godbolt.org/z/fsroE4seh:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/fsroE4seh 
#include <iostream>

// Production code:
struct DatabaseInterface {
    virtual int getData() const = 0;
};
struct RealDatabase : public DatabaseInterface {
    int getData() const override { return 999; }
};
struct Program {
    DatabaseInterface &db_;
    void run() {
        std:: cout << db_.getData() << "\n";
    }
};

// main as test:
int main() {
    RealDatabase db;
    Program prog { db }; // real DB is tested along
    prog.run();            // Expected output: 999
}

GodboltId:sTaoeqfxr

Book listing lst-0003-book.cpp:

// https://godbolt.org/z/sTaoeqfxr 
void testDoubleIt5() {
    // arrange
    auto param = 5;
    // act
    auto result = doubleIt(param);
    // assert
    assertTrue(result == 10);
}

Godbolt Listing lst-0003-godb.cpp, https://godbolt.org/z/sTaoeqfxr:

//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/sTaoeqfxr 
void testDoubleIt5() {
    // arrange
    auto param = 5;
    // act
    auto result = doubleIt(param);
    // assert
    assertTrue(result == 10);
}

GodboltId:rn5oK3M6e

Book listing lst-0004-book.cpp:

// https://godbolt.org/z/rn5oK3M6e 
void testDoubleIt() {
    assertTrue( doubleIt(0) == 0 );
    assertTrue( doubleIt(-1) == -2 );
    assertTrue( doubleIt(1) == 2 );
    assertTrue( doubleIt(5) == 10 );
}

Godbolt Listing lst-0004-godb.cpp, https://godbolt.org/z/rn5oK3M6e:

//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/rn5oK3M6e 
void testDoubleIt() {
    assertTrue( doubleIt(0) == 0 );
    assertTrue( doubleIt(-1) == -2 );
    assertTrue( doubleIt(1) == 2 );
    assertTrue( doubleIt(5) == 10 );
}

Listing 14.3: In the “fib.cpp” file, there is a free function that you want to test.

Book listing lst-0005-book.cpp:

// https://godbolt.org/z/Ex6Mv6xcz 
unsigned fib(unsigned n) {
    if(n==0) { return 0; }
    if(n==1) { return 1; }
    unsigned a = 0;
    unsigned b = 1;
    unsigned sum = 1;
    while(n>1) {
        sum += a;
        a = b;
        b = sum;
        n -= 1;
    }
    return sum;
}

Godbolt Listing lst-0005-godb.cpp, https://godbolt.org/z/Ex6Mv6xcz:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Ex6Mv6xcz 
unsigned fib(unsigned n) {
    if(n==0) { return 0; }
    if(n==1) { return 1; }
    unsigned a = 0;
    unsigned b = 1;
    unsigned sum = 1;
    while(n>1) {
        sum += a;
        a = b;
        b = sum;
        n -= 1;
    }
    return sum;
}

GodboltId:erPdKo1qh

Book listing lst-0006-book.cpp:

// https://godbolt.org/z/erPdKo1qh (uses boost library) 
#define BOOST_TEST_MAIN test_main            // generates main() in this module
#include <boost/test/included/unit_test.hpp> // framework
#include <boost/test/test_tools.hpp>         // BOOST_CHECK etc
unsigned fib(unsigned n);                    // to test
namespace {
using namespace boost::unit_test;
BOOST_AUTO_TEST_CASE( test_fib_low )         // arbitrary name of the test case
{
    BOOST_CHECK( fib(0) == 0 );               // individual assertions
    BOOST_CHECK( fib(1) == 1 );
    BOOST_CHECK( fib(2) == 1 );
    BOOST_CHECK( fib(3) == 2 );
    BOOST_CHECK( fib(4) == 3 );
    BOOST_CHECK( fib(5) == 5 );
    BOOST_CHECK( fib(6) == 8 );
}
}

Godbolt Listing lst-0006-godb.cpp, https://godbolt.org/z/erPdKo1qh:

//#(compile) c++; compiler:g141; options:-O0 -std=c++23; libs:boost@184
// https://godbolt.org/z/erPdKo1qh (uses boost library) 
#define BOOST_TEST_MAIN test_main            // generates main() in this module
#include <boost/test/included/unit_test.hpp> // framework
#include <boost/test/test_tools.hpp>         // BOOST_CHECK etc
unsigned fib(unsigned n);                    // to test
namespace {
using namespace boost::unit_test;
BOOST_AUTO_TEST_CASE( test_fib_low )         // arbitrary name of the test case
{
    BOOST_CHECK( fib(0) == 0 );               // individual assertions
    BOOST_CHECK( fib(1) == 1 );
    BOOST_CHECK( fib(2) == 1 );
    BOOST_CHECK( fib(3) == 2 );
    BOOST_CHECK( fib(4) == 3 );
    BOOST_CHECK( fib(5) == 5 );
    BOOST_CHECK( fib(6) == 8 );
}
}

Listing 14.4: The main header file of the qwort.hpp library.

Book listing lst-0012-book.cpp:

// https://godbolt.org/z/j93cYaWd4 
#ifndef QWORT_H   // Header guard
#define QWORT_H
#include <string>
#include <string_view>
#include <memory> // unique_ptr
namespace qw {    // Namespace of the library

    int version();

    namespace impl_multimap {
        class index_impl;
    }

    class index {
        using index_impl = impl_multimap::index_impl;
    public:
        index();
        ~index() noexcept; // needed for pimpl
        index(const index&) = delete;
        index(index&&) noexcept;
        index& operator=(const index&) = delete;
        index& operator=(index&&) = delete;
    public:
        void add(std::string_view arg);
        size_t size() const;
        std::string getBestMatch(std::string_view query) const;
    public:          // public for tests
        std::string normalize(std::string arg) const;
    private:
        std::unique_ptr<index_impl> pimpl;
    };
} // namespace qw
#endif // Header guard

Godbolt Listing lst-0012-godb.cpp, https://godbolt.org/z/j93cYaWd4:

//#(execute) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/j93cYaWd4 
#ifndef QWORT_H   // Header guard
#define QWORT_H
#include <string>
#include <string_view>
#include <memory> // unique_ptr
namespace qw {    // Namespace of the library

    int version();

    namespace impl_multimap {
        class index_impl;
    }

    class index {
        using index_impl = impl_multimap::index_impl;
    public:
        index();
        ~index() noexcept; // needed for pimpl
        index(const index&) = delete;
        index(index&&) noexcept;
        index& operator=(const index&) = delete;
        index& operator=(index&&) = delete;
    public:
        void add(std::string_view arg);
        size_t size() const;
        std::string getBestMatch(std::string_view query) const;
    public:          // public for tests
        std::string normalize(std::string arg) const;
    private:
        std::unique_ptr<index_impl> pimpl;
    };
} // namespace qw
#endif // Header guard

Listing 14.5: Public interface for testing only.

Book listing lst-0013-book.cpp:

class index_impl {
// ...
public: // test interface
    vector<string> _qgramify(string_view n) const { return qgramify(n); }
    static size_t _q() { return Q; }
    static std::string _prefix() { return PREFIX; }
    static std::string _suffix() { return SUFFIX; }
};

Listing 14.6: This test module tests qwort.cpp.

Book listing lst-0014-book.cpp:

// https://godbolt.org/z/TacMfah8E (uses boost library) 
#include "qwort/qwort.hpp" // under test
#define BOOST_TEST_MODULE qwort
#include <boost/test/included/unit_test.hpp>
using namespace boost::unit_test;
BOOST_AUTO_TEST_CASE( version_is_1 ) {
    BOOST_TEST(qw::version() == 1);
    // the following comparison should fail, but only produce a warning:
    BOOST_WARN_EQUAL(qw::version(), 2);   // (ERR) not equal, but continues
}
BOOST_AUTO_TEST_CASE( init_size_0 ) {
    qw::index inst{};                      // arrange
    auto sz = inst.size();                 // act
    BOOST_TEST(sz == 0u);                  // assert
}
BOOST_AUTO_TEST_CASE( add_size_1 ) {
    using namespace std::literals::string_literals;
    qw::index inst{};                      // arrange
    inst.add(""s);                         // act
    BOOST_REQUIRE_EQUAL(inst.size(), 1u); // assert
}
BOOST_AUTO_TEST_CASE( normalize ) {
    using namespace std::literals::string_literals;
    qw::index inst{}; // arrange
    // acts and asserts; could also be in separate functions
    BOOST_CHECK_EQUAL(inst.normalize("a"s), "A"s);
    BOOST_CHECK_EQUAL(inst.normalize("Town"s), "TOWN"s);
    BOOST_CHECK_EQUAL(inst.normalize("White Space"s), "WHITE#SPACE"s);
    BOOST_CHECK_EQUAL(inst.normalize("!Sym-bol."s), "#SYM#BOL#"s);
}
BOOST_AUTO_TEST_CASE( move ) {
    qw::index inst{};
    qw::index other = std::move( inst );
    BOOST_CHECK_EQUAL(other.size(), 0u); // pimpl successfully moved?
}

Godbolt Listing lst-0014-godb.cpp, https://godbolt.org/z/TacMfah8E:

//#(compile) c++; compiler:g141; options:-O0 -std=c++23; libs:boost@184
// https://godbolt.org/z/TacMfah8E (uses boost library) 
#include "qwort/qwort.hpp" // under test
#define BOOST_TEST_MODULE qwort
#include <boost/test/included/unit_test.hpp>
using namespace boost::unit_test;
BOOST_AUTO_TEST_CASE( version_is_1 ) {
    BOOST_TEST(qw::version() == 1);
    // the following comparison should fail, but only produce a warning:
    BOOST_WARN_EQUAL(qw::version(), 2);   // (ERR) not equal, but continues
}
BOOST_AUTO_TEST_CASE( init_size_0 ) {
    qw::index inst{};                      // arrange
    auto sz = inst.size();                 // act
    BOOST_TEST(sz == 0u);                  // assert
}
BOOST_AUTO_TEST_CASE( add_size_1 ) {
    using namespace std::literals::string_literals;
    qw::index inst{};                      // arrange
    inst.add(""s);                         // act
    BOOST_REQUIRE_EQUAL(inst.size(), 1u); // assert
}
BOOST_AUTO_TEST_CASE( normalize ) {
    using namespace std::literals::string_literals;
    qw::index inst{}; // arrange
    // acts and asserts; could also be in separate functions
    BOOST_CHECK_EQUAL(inst.normalize("a"s), "A"s);
    BOOST_CHECK_EQUAL(inst.normalize("Town"s), "TOWN"s);
    BOOST_CHECK_EQUAL(inst.normalize("White Space"s), "WHITE#SPACE"s);
    BOOST_CHECK_EQUAL(inst.normalize("!Sym-bol."s), "#SYM#BOL#"s);
}
BOOST_AUTO_TEST_CASE( move ) {
    qw::index inst{};
    qw::index other = std::move( inst );
    BOOST_CHECK_EQUAL(other.size(), 0u); // pimpl successfully moved?
}

Listing 14.7: The test suite is manually assembled here.

Book listing lst-0024-book.cpp:

// https://godbolt.org/z/r9sdhaazz (uses boost library)
/* private header from lib directory: */
#include "impl_multimap.hpp"                  // to test
// we define init_unit_test_suite() ourselves, so DO NOT set:
/* #define BOOST_TEST_MODULE qgram */
#include <boost/test/included/unit_test.hpp>
#include <memory>                             // shared ptr
#include <vector>
#include <string>
using namespace boost::unit_test;
using namespace std::literals::string_literals;
using std::string; using std::vector;
/* === A test class for the class under test === */
using UnderTest = qw::impl_multimap::index_impl;
class ImplMultimapTest {                      // class with test methods
public:
    /* === Constants === */
    void testConstants() {
        BOOST_REQUIRE_EQUAL(UnderTest::_prefix().length(), UnderTest::_q()-1);
        BOOST_REQUIRE_EQUAL(UnderTest::_suffix().length(), UnderTest::_q()-1);
        for(size_t i = 0; i < UnderTest::_q()-1; ++i) {
            BOOST_CHECK_EQUAL(UnderTest::_prefix()[i], '^');
            BOOST_CHECK_EQUAL(UnderTest::_suffix()[i], '$');
        }
        // or more specifically:
        BOOST_TEST(UnderTest::_q() == 3u);
        BOOST_TEST(UnderTest::_prefix() == "^^"s);
        BOOST_TEST(UnderTest::_suffix() == "$$"s);
    }
    /* === qgramify === */
    void testQgramifyEmpty() {
        UnderTest inst{};
        auto result = inst._qgramify(""s);
        vector<string> expected{"^^$"s, "^$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify1() {
        UnderTest inst{};
        auto result = inst._qgramify("a"s);
        vector<string> expected{"^^a"s, "^a$"s, "a$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify2() {
        UnderTest inst{};
        auto result = inst._qgramify("ab"s);
        vector<string> expected{"^^a"s, "^ab"s, "ab$"s, "b$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify3() {
        UnderTest inst{};
        auto result = inst._qgramify("abc"s);
        vector<string> expected{"^^a"s, "^ab"s, "abc"s, "bc$"s, "c$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    /* === add === */
    void testAdd_nodups() {
        UnderTest inst{};                     /* arrange */
        BOOST_REQUIRE_EQUAL(inst.size(), 0u); /* assert */
        inst.add("", "");                     /* act */
        BOOST_CHECK_EQUAL(inst.size(), 1u);   /* assert */
        inst.add("ENTRY", "entry");           /* act */
        BOOST_CHECK_EQUAL(inst.size(), 2u);   /* assert */
        inst.add("OTHER", "other");           /* act */
        BOOST_CHECK_EQUAL(inst.size(), 3u);   /* assert */
    }
    /* === add === */
    void test_getBestMatch_empty() {
        UnderTest inst{};
        auto result = inst.getBestMatch("any");
        BOOST_CHECK_EQUAL(result, ""s);
    }
    void test_getBestMatch_one() {
        /* arrange */
        UnderTest inst{};
        inst.add("HOLSDERTEUFEL", "holsderteufel");
        /* act */
        auto result = inst.getBestMatch("ROBERT");
        BOOST_CHECK_EQUAL(result, "holsderteufel"s);
    }
    void test_getBestMatch_exact() {
        /* arrange */
        UnderTest inst{};
        inst.add("BERLIN", "Berlin");
        inst.add("HAMBURG", "Hamburg");
        inst.add("DORTMUND", "Dortmund");
        inst.add("STUTTGART", "Stuttgart");
        inst.add("WYK", "Wyk");
        /* act and assert */
        BOOST_CHECK_EQUAL(inst.getBestMatch("BERLIN"), "Berlin"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("HAMBURG"), "Hamburg"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("DORTMUND"), "Dortmund"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("STUTTGART"), "Stuttgart"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("WYK"), "Wyk"s);
    }
    void test_getBestMatch_close() {
        /* arrange */
        UnderTest inst{};
        inst.add("BERLIN", "Berlin");
        inst.add("HAMBURG", "Hamburg");
        inst.add("DORTMUND", "Dortmund");
        inst.add("STUTTGART", "Stuttgart");
        inst.add("WYK", "Wyk");
        /* act and assert */
        BOOST_CHECK_EQUAL(inst.getBestMatch("BRLIN"), "Berlin"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("BURG"), "Hamburg"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("DORTDORT"), "Dortmund"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("STUTGURT"), "Stuttgart"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("WIK"), "Wyk"s);
    }
/* For new test methods: Don't forget to add to init_unit_test_suite() */
};
/* === Suite === */
test_suite* init_unit_test_suite( int argc, char* argv[] ) {
    auto tester = std::make_shared<ImplMultimapTest>();
    auto &ts = framework::master_test_suite();
    ts.add( BOOST_TEST_CASE( [=](){ tester->testConstants(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramifyEmpty(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify1(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify2(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify3(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testAdd_nodups(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_empty(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_one(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_exact(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_close(); } ));
    return nullptr;
}

Godbolt Listing lst-0024-godb.cpp, https://godbolt.org/z/r9sdhaazz:

//#(compile) c++; compiler:g141; options:-O0 -std=c++23; libs:boost@184
// https://godbolt.org/z/r9sdhaazz (uses boost library)
/* private header from lib directory: */
#include "impl_multimap.hpp"                  // to test
// we define init_unit_test_suite() ourselves, so DO NOT set:
/* #define BOOST_TEST_MODULE qgram */
#include <boost/test/included/unit_test.hpp>
#include <memory>                             // shared ptr
#include <vector>
#include <string>
using namespace boost::unit_test;
using namespace std::literals::string_literals;
using std::string; using std::vector;
/* === A test class for the class under test === */
using UnderTest = qw::impl_multimap::index_impl;
class ImplMultimapTest {                      // class with test methods
public:
    /* === Constants === */
    void testConstants() {
        BOOST_REQUIRE_EQUAL(UnderTest::_prefix().length(), UnderTest::_q()-1);
        BOOST_REQUIRE_EQUAL(UnderTest::_suffix().length(), UnderTest::_q()-1);
        for(size_t i = 0; i < UnderTest::_q()-1; ++i) {
            BOOST_CHECK_EQUAL(UnderTest::_prefix()[i], '^');
            BOOST_CHECK_EQUAL(UnderTest::_suffix()[i], '$');
        }
        // or more specifically:
        BOOST_TEST(UnderTest::_q() == 3u);
        BOOST_TEST(UnderTest::_prefix() == "^^"s);
        BOOST_TEST(UnderTest::_suffix() == "$$"s);
    }
    /* === qgramify === */
    void testQgramifyEmpty() {
        UnderTest inst{};
        auto result = inst._qgramify(""s);
        vector<string> expected{"^^$"s, "^$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify1() {
        UnderTest inst{};
        auto result = inst._qgramify("a"s);
        vector<string> expected{"^^a"s, "^a$"s, "a$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify2() {
        UnderTest inst{};
        auto result = inst._qgramify("ab"s);
        vector<string> expected{"^^a"s, "^ab"s, "ab$"s, "b$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    void testQgramify3() {
        UnderTest inst{};
        auto result = inst._qgramify("abc"s);
        vector<string> expected{"^^a"s, "^ab"s, "abc"s, "bc$"s, "c$$"s};
        BOOST_CHECK_EQUAL_COLLECTIONS(
            result.begin(), result.end(), expected.begin(), expected.end() );
    }
    /* === add === */
    void testAdd_nodups() {
        UnderTest inst{};                     /* arrange */
        BOOST_REQUIRE_EQUAL(inst.size(), 0u); /* assert */
        inst.add("", "");                     /* act */
        BOOST_CHECK_EQUAL(inst.size(), 1u);   /* assert */
        inst.add("ENTRY", "entry");           /* act */
        BOOST_CHECK_EQUAL(inst.size(), 2u);   /* assert */
        inst.add("OTHER", "other");           /* act */
        BOOST_CHECK_EQUAL(inst.size(), 3u);   /* assert */
    }
    /* === add === */
    void test_getBestMatch_empty() {
        UnderTest inst{};
        auto result = inst.getBestMatch("any");
        BOOST_CHECK_EQUAL(result, ""s);
    }
    void test_getBestMatch_one() {
        /* arrange */
        UnderTest inst{};
        inst.add("HOLSDERTEUFEL", "holsderteufel");
        /* act */
        auto result = inst.getBestMatch("ROBERT");
        BOOST_CHECK_EQUAL(result, "holsderteufel"s);
    }
    void test_getBestMatch_exact() {
        /* arrange */
        UnderTest inst{};
        inst.add("BERLIN", "Berlin");
        inst.add("HAMBURG", "Hamburg");
        inst.add("DORTMUND", "Dortmund");
        inst.add("STUTTGART", "Stuttgart");
        inst.add("WYK", "Wyk");
        /* act and assert */
        BOOST_CHECK_EQUAL(inst.getBestMatch("BERLIN"), "Berlin"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("HAMBURG"), "Hamburg"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("DORTMUND"), "Dortmund"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("STUTTGART"), "Stuttgart"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("WYK"), "Wyk"s);
    }
    void test_getBestMatch_close() {
        /* arrange */
        UnderTest inst{};
        inst.add("BERLIN", "Berlin");
        inst.add("HAMBURG", "Hamburg");
        inst.add("DORTMUND", "Dortmund");
        inst.add("STUTTGART", "Stuttgart");
        inst.add("WYK", "Wyk");
        /* act and assert */
        BOOST_CHECK_EQUAL(inst.getBestMatch("BRLIN"), "Berlin"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("BURG"), "Hamburg"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("DORTDORT"), "Dortmund"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("STUTGURT"), "Stuttgart"s);
        BOOST_CHECK_EQUAL(inst.getBestMatch("WIK"), "Wyk"s);
    }
/* For new test methods: Don't forget to add to init_unit_test_suite() */
};
/* === Suite === */
test_suite* init_unit_test_suite( int argc, char* argv[] ) {
    auto tester = std::make_shared<ImplMultimapTest>();
    auto &ts = framework::master_test_suite();
    ts.add( BOOST_TEST_CASE( [=](){ tester->testConstants(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramifyEmpty(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify1(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify2(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testQgramify3(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->testAdd_nodups(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_empty(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_one(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_exact(); } ));
    ts.add( BOOST_TEST_CASE( [=](){ tester->test_getBestMatch_close(); } ));
    return nullptr;
}

Listing 14.8: A test function with a parameter can be called with test data.

Book listing lst-0029-book.cpp:

// https://godbolt.org/z/5GondGsMz (uses boost library)
#include <boost/test/parameterized_test.hpp>
struct Param {
    string input;
    vector<string> expected;
};
const vector<Param> params {
    // { input, expected result }
    {""s,     {"^^$"s, "^$$"s} },
    {"A"s,    {"^^A"s, "^A$"s, "A$$"s} },
    {"AB"s,   {"^^A"s, "^AB"s, "AB$"s, "B$$"s} },
    {"ACB"s,  {"^^A"s, "^AC"s, "ACB"s, "CB$"s, "B$$"s} },
    {"AAA"s,  {"^^A"s, "^AA"s, "AAA"s, "AA$"s, "A$$"s} },
};
void testQgramify(const Param& param) {
    /* arrange */
    UnderTest inst{};
    /* act */
    auto result = inst._qgramify(param.input);
    /* assert */
    BOOST_CHECK_EQUAL_COLLECTIONS(
        param.expected.begin(), param.expected.end(),
        result.begin(), result.end());
}

Godbolt Listing lst-0029-godb.cpp, https://godbolt.org/z/5GondGsMz:

//#(compile) c++; compiler:g141; options:-O0 -std=c++23; libs:boost@184
// https://godbolt.org/z/5GondGsMz (uses boost library)
#include <boost/test/parameterized_test.hpp>
struct Param {
    string input;
    vector<string> expected;
};
const vector<Param> params {
    // { input, expected result }
    {""s,     {"^^$"s, "^$$"s} },
    {"A"s,    {"^^A"s, "^A$"s, "A$$"s} },
    {"AB"s,   {"^^A"s, "^AB"s, "AB$"s, "B$$"s} },
    {"ACB"s,  {"^^A"s, "^AC"s, "ACB"s, "CB$"s, "B$$"s} },
    {"AAA"s,  {"^^A"s, "^AA"s, "AAA"s, "AA$"s, "A$$"s} },
};
void testQgramify(const Param& param) {
    /* arrange */
    UnderTest inst{};
    /* act */
    auto result = inst._qgramify(param.input);
    /* assert */
    BOOST_CHECK_EQUAL_COLLECTIONS(
        param.expected.begin(), param.expected.end(),
        result.begin(), result.end());
}