15

What would be the proper way to access an external test file for the unit test of a c++ project? I am using CMake and Gtest.

This is a sample of the directory structure.

Project
   -src
       -test (unit tests here)
   -test-data (data file here)

Thanks!

Teancum
  • 245
  • 1
  • 2
  • 9

5 Answers5

11

I prefer to find my test data relative to my executable test. To do so, I usually define a helper method in some TestHelpers.h and then pass the relative path of the file I'm looking to resolve.

inline std::string resolvePath(const std::string &relPath)
{
    namespace fs = std::tr2::sys;
    // or namespace fs = boost::filesystem;
    auto baseDir = fs::current_path();
    while (baseDir.has_parent_path())
    {
        auto combinePath = baseDir / relPath;
        if (fs::exists(combinePath))
        {
            return combinePath.string();
        }
        baseDir = baseDir.parent_path();
    }
    throw std::runtime_error("File not found!");
}

To use it, I go:

std::string foofullPath = resolvePath("test/data/foo.txt");

and that gives me a full path of the test file as long as my executing directory runs from on a descendant of the project's root directory.

Darien Pardinas
  • 5,910
  • 1
  • 41
  • 48
9

In your CMakefile, add your tests and set some an environment variable with the path to you data.

add_test(mytests ${PROJECT_BINARY_DIR}/unittests)
set_tests_properties(mytests PROPERTIES 
                     ENVIRONMENT
                     DATADIR=${CMAKE_CURRENT_SOURCE_DIR}/tests/testvectors)

You can later retrieve the DATADIR from the environment in any test.

You other option is to define a different working directory

set_tests_properties(mytests PROPERTIES
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)

In my opinion, this is the less intrusive and simpler way.

Juan Leni
  • 6,982
  • 5
  • 55
  • 87
  • I'm not having luck with set_tests_properties() cmake command with either the `ENVIRONMENT` or the `WORKING_DIRECTORY` directives. Neither one seem to work in Visual Studio 2019. I've dumped all environment variables from within the runner and dumped the current working directory from within the runner, and they both remain unchanged. I suspect it is an issue with Visual Studio and not cmake however. – skelliam Jan 26 '22 at 16:35
7

Pass the file name to gtest arguments:

add_executable(foo ...)
enable_testing()
add_test(FooTest foo "${CMAKE_CURRENT_LIST_DIR}/data/input.file")

get the parameter after gtest parse input:

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  assert(argc == 2); // gtest leaved unparsed arguments for you

and save it to some global *:

  file_name = argv[1];
  return RUN_ALL_TESTS();

* Usually it's not a very good idea to pollute the global namespace but I think it's fine for testing app

Related

Community
  • 1
  • 1
  • This looks great! When I try to implement it argv does not have the argument. Any suggestions? – Teancum Aug 11 '14 at 21:42
  • the `argc/argv` are the same before and after the `InitGoogleTest` each time they only include the executable path and not any arguments – Teancum Aug 12 '14 at 15:02
  • I am running the test in Xcode as a part of the project. I think that it runs the executable directly. When I run Ctest from the command line it says no tests were found. I may be missing something with a proper Ctest setup, but if you could direct me to a good example of a project that is doing what I am trying to do(with c++, Gtest, CMake, and external test files) I should be able to figure it out. I just can't find a good example. Thanks! – Teancum Aug 12 '14 at 18:52
2

You could use the CMAKE_SOURCE_DIR (gives absolute path of the top level cmake directory) variable to define the path and pass it to the test script.

lazycoder
  • 336
  • 2
  • 6
  • How would I access this from the c++ code? Could you help me with a simple example? – Teancum Aug 08 '14 at 20:01
  • You can't access that from the c++ code directly, you would need to somehow feed to the binary that runs the test. I've never used Gtest so I don't how that might be possible in that context. If the data is you need is not very large you also could copy there via CMake (see `file(COPY ...)`). There are a lot ways you can achieve this, but depends very heavily on your CMake/build/Project/Test structure how to make it _good_ . – lazycoder Aug 08 '14 at 20:13
  • I am trying to access the file by the relative path in the c++ code so I do not need to hard code the file path based on my system. `open(test-data/dataFile.txt)` – Teancum Aug 08 '14 at 20:40
  • Based on the sample structure you posted, you can just do something like `open(../../test-data/dataFile.txt)`. But I assumed you are looking for something more flexible. – lazycoder Aug 08 '14 at 21:10
0

Just specify a WORKING_DIRECTORY in the add_test function. Any path specification in the test cases will then be relative to the specified WORKING_DIRECTORY.

For example: In your root CMakeLists.txt

# ...
add_executable(<name of your test executable> src/test/test.cpp)

# ...

add_test(NAME <arbitrary name> COMMAND <name of your test executable> WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/src/test/")

And then in your google tests use paths like "../test-data/expected_values.csv"

Dawid
  • 477
  • 3
  • 14