0

I have this project for university which we have to make a small game in C++. I'm trying to use Test Driven Development for the most fundamental parts of the game so I was trying to set up Catch2, I already set up CMake and it works just fine, then I set up Catch2 v3 but it isn't working properly

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

project(game VERSION 0.1)

file(GLOB_RECURSE SRC_FILES src/*.cpp)
add_executable(game main.cpp ${SRC_FILES})
target_include_directories(game PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include())


find_package(Catch2 3 REQUIRED)
# These tests can use the Catch2-provided main
add_executable(tests ../testing/test.cpp)
target_include_directories(tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include())
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)

The following is my test.cpp file (with only one of the test cases so this post doesn't get huge):

/* Include the unit testing libraries */
#define CATCH_CONFIG_MAIN
#include <catch2/catch_test_macros.hpp>

/* Include the code that we plan to test */
#include "worldmap.h"

/** of course we could check every single possible value, but this isn't 
 * going to be an option in most cases as we might have to check
 * billions of possible values which isn't realistic **/
TEST_CASE( "1: Children (worldmap.h)", "[multi-file:1]" )
{
    const std::string expectedAnswer = "child";
    const int minimum = 5;
    const int maximum = 12;
    
    WHEN( "Is a child, 5 to 12 years")
    {
        for( int test=minimum; test<=maximum; ++test )
        {
            INFO( "Age is " << test );
            REQUIRE( ageDesc(test) == expectedAnswer );
        }
    }

    WHEN( "Is not a child edge cases" )
    {
        // making sure that just outside the min and max of the range is correct
        INFO( "Age is " << minimum-1 );
        REQUIRE( ageDesc(minimum-1) != expectedAnswer );

        INFO( "Age is " << maximum+1 );
        REQUIRE( ageDesc(maximum+1) != expectedAnswer );
    }
}

worldmap.cpp:

#include <string>
#include "worldmap.h"

std::string ageDesc( int age )
{
    if( age < 0 || age > 122) return "lying";
    if( age < 5 ) return "baby";
    if( age >= 5 && age <= 12 ) return "child";
    if( age >= 13 && age <= 19 ) return "teenager";
    if( age >= 65 && age <= 122 ) return "oap";
    
    return "adult";
}

worldmap.h:

#ifndef WORLDMAP_H
#define WORLDMAP_H

std::string ageDesc( int age );

#endif

main.cpp:

#include <iostream>
#include "worldmap.h"

int main() {
    std::cout << "\nHow old are you? ";

    int age;
    std::cin >> age;
    std::cout<<ageDesc(age)<<std::endl;
    return 0;
}

My file structure is as follows:

-- main.cpp

  • src -- worldmap.cpp
  • include -- worldmap.h
  • testing -- test.cpp
  • build -- all cmake files

My main.cpp, worldmap.cpp and worldmap.h are correctly "linked" and they work just fine I've tested them before and when using cmake i was able to compile everything correctly and even run the game (by the way the age thing is just the code provided by my university when showing testing files, I couldn't come up with a better test at the moment so I just used theirs, essentially it just asks the user what their age is and returns whether they're a baby, child, teen, adult, OAP, etc...) The problem is that my test file gives me the error:

undefined reference to 'ageDesc[abi:cxx11](int)'

but it is defined in worldmap.h which is being included in the test file. I have googled and looked at the documentation but haven't found anything about this. The only proper answer on google mentioned how the #define CATCH_CONFIG_MAIN should come before #include <catch2.hpp> but that was for v3, so I'm using #include <catch2/catch_test_macros.hpp> and I've tried to both use and remove the CATCH_CONFIG_MAIN definition and it still gives me the same error.

Rulz
  • 1
  • 3
    first you need to understand the difference between a declaration (what you have in `worldmap.h`) and a definition (the thing that is missing). – 463035818_is_not_an_ai Feb 14 '23 at 15:22
  • 1
    Do you build with the `worldmap.cpp` source file? – Some programmer dude Feb 14 '23 at 15:24
  • 1
    My recollection is that `file(GLOB...` doesn't detect changes automatically when you add new files to the `/src` folder. You need to rerun cmake in that case, so that it can pick them up. I'd also recommend printing out `SRC_FILES` to make sure it actually has the file(s) you expect. – ChrisMM Feb 14 '23 at 15:26
  • A side note, what was wrong with [googletest](https://github.com/google/googletest) ? – pptaszni Feb 14 '23 at 15:29
  • @pptaszni google test requires lots of boilerplate. Catch2 is nicer and easier to use. On other head tests in gtest are scaling better, so it is more useful with complex logic. Try it yourself: https://godbolt.org/z/8GEGTK6TM – Marek R Feb 14 '23 at 15:43
  • To fix issue, here is a hacky solution: `add_executable(tests ../testing/test.cpp ${SRC_FILES})`. To properly fix it: library should be introduced and linked to `game` target and `tests` target. – Marek R Feb 14 '23 at 15:46
  • @Rulz proper form of test for that: https://godbolt.org/z/a33j39Whq – Marek R Feb 14 '23 at 16:23
  • Google Test is good, Boost Test is good, Catch2 is good (and lighter weight than the first two). I like doctest, which is also lighter weight. – Eljay Feb 14 '23 at 17:09

0 Answers0