-
-
Notifications
You must be signed in to change notification settings - Fork 642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can I mix production code with the Unit Tests? #53
Comments
If you are using CMake you could add
The same can be done in any (meta) build system. You could also create a "production" config which is like "release" (by copying those flags) but with the addition of this configuration identifier. If you are using directly Visual Studio you could add the define for a particular config globally. Same for all other IDEs. Does this help? |
Yes. For now I am only using a simple make file I wrote by hand. But I am looking for the available tools for Makefile generation. I think I found the solution, to include the #if !( DEBUG_LEVEL & DEBUG_LEVEL_RUN_UNIT_TESTS )
#define DOCTEST_CONFIG_DISABLE
#endif
#include "doctest.h" So, I am not including the header Now all my project source code files are like these: doctest_main.cpp /**
* This tells to provide a main() - Only do this in one cpp file.
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h" doctest_tests.cpp #include "project_sources_with_tests.cpp"
//... project_sources_with_tests.cpp #include "debug.h"
unsigned int factorial( unsigned int number )
{
return number <= 1 ? number : factorial(number-1)*number;
}
TEST_CASE("testing the factorial function")
{
CHECK(factorial(1) == 1);
CHECK(factorial(2) == 2);
CHECK(factorial(3) == 6);
CHECK(factorial(10) == 3628800);
} debug.h /**
* This is to view internal program data while execution. Default value: 0
*
* 0 - Disables this feature.
* 1 - Basic debugging.
* 2 - Run the `doctest` Unit Tests.
*/
#define DEBUG_LEVEL 1
#define DEBUG_LEVEL_DISABLED_DEBUG 0
#define DEBUG_LEVEL_BASIC_DEBUG 1
#define DEBUG_LEVEL_RUN_UNIT_TESTS 2
#if DEBUG_LEVEL > DEBUG_LEVEL_DISABLED_DEBUG
#define DEBUG
/**
* Disables the `doctest` Unit Tests from being included/compiled to the binary output file.
*
* See:
* https://github.com/onqtam/doctest/blob/master/doc/markdown/configuration.md
*/
#if !( DEBUG_LEVEL & DEBUG_LEVEL_RUN_UNIT_TESTS )
#define DOCTEST_CONFIG_DISABLE
#endif
#include "doctest.h"
#endif This is the Makefile PROGRAM_MAIN_DEBUGGER = debug
DOCTEST_TESTS_FILE = doctest_tests
DOCTEST_DRIVER_CLASS = doctest_main
doctest_tests: $(DOCTEST_DRIVER_CLASS).o $(PROGRAM_MAIN_DEBUGGER).h
g++ --std=c++11 $(DOCTEST_DRIVER_CLASS).o $(DOCTEST_TESTS_FILE).cpp -o main
./main --force-colors=true
$(DOCTEST_DRIVER_CLASS).o: $(DOCTEST_DRIVER_CLASS).cpp
g++ $(DOCTEST_DRIVER_CLASS).cpp -c -o $(DOCTEST_DRIVER_CLASS).o |
I was about to ask a similar question. I made a multi-file test case here: The main program has two modes depending on whether "TEST" is defined. This is to emulate multiple binaries. Consider that I have a test executable which defines "TEST" and a production executable that doesn't. The doctest asserts are in class.cpp so that I can test anonymous-scoped functions. I'm trying to emulate the situation I have with my real code, but perhaps wandbox is oversimplifying it... I am trying to add doctest tests to an existing codebase with 100s of test files. We use Boost.Buildwith custom test macros rather than Boost.Test. My current build system would only build class.cpp once even though it's used by both the test build and the production binary. The doctest asserts cannot go in the test files of course, they have to go in the actual TUs that have the anonymous functions to be tested. My original thought was to put the doctest runner in the individual test files (which are used to test the public APIs), but that will result in doctest asserts being run multiple times. According to the discussion above, I need to modify the build config so that these TUs get built twice - once for the production code and once for the test code. Most of my TUs probably won't have doctest asserts, so this seems pretty painful. I guess I need to globally include and disable doctest, which can be overridden by a special build system function to create a testing bootstrapper for each TU I put doctests in. Is there a better way I'm missing? |
You should put the running on the Driver Class, which starts the Unit Tests execution, not on the main file where you program starts' running. However you can put it there if you use a global variable to control whether the Unit Tests are enabled. For example, on my code I have a global include called debug.h /**
* This is to view internal program data while execution. Default value: 0
*
* 0 - Disables this feature.
* 1 - Basic debugging.
* 2 - Run the `doctest` Unit Tests.
*/
#define DEBUG_LEVEL 1
#if DEBUG_LEVEL > 0
#define DEBUG
/**
* Disables the `doctest` Unit Tests from being included/compiled to the binary output file.
*
* See:
* https://github.com/onqtam/doctest/blob/master/doc/markdown/configuration.md
*/
#if !( DEBUG_LEVEL & 2 )
#define DOCTEST_CONFIG_DISABLE
#endif
#endif
#include "doctest.h" Now every file on my project includes this When I set the Now the problem is how to start the Unit Tests. This is done automatically by a driver class called doctest_driver_class.cpp #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h" So, when you build it you need to first build the driver class with: g++ doctest_driver_class.cpp -c -o doctest_driver_class.o And later compile/link together all the project files which include the Unit Tests. For example, let us assume do you have only one project file with Unit Tests and it is called source_file_with_tests.cpp #include "debug.h"
unsigned int factorial( unsigned int number )
{
return number <= 1 ? number : factorial(number-1)*number;
}
TEST_CASE("testing the factorial function")
{
CHECK(factorial(1) == 1);
CHECK(factorial(2) == 2);
CHECK(factorial(3) == 6);
CHECK(factorial(10) == 3628800);
} Now to link together this project source file, you need to run the command: g++ --std=c++11 doctest_driver_class.o source_file_with_tests.cpp -o main And if you ran the If you have more files with unit tests like g++ --std=c++11 doctest_driver_class.o source_file_with_tests.cpp other_unit_test.cpp -o main
I did not understand why to create a special bootstrapper for each TU you put doctest in. Perhaps the example I put above solves this problem? |
Because I plan to have the doctests spread across my project hierarchy, it would be awkward to collect them all and run them once. Instead, I am compiling a second version of the TUs with doctests with a special flag, like your debug flag, to turn on doctest. Otherwise it will be disabled. // without PWIZ_DOCTEST defined, disable doctest macros; when it is defined, doctest will be configured with main()
#ifndef PWIZ_DOCTEST
#define DOCTEST_CONFIG_DISABLE
#include "libraries/doctest.h"
#else
#define DOCTEST_CONFIG_IMPLEMENT
#include "libraries/doctest.h"
int main(int argc, char* argv[])
{
TEST_PROLOG(argc, argv)
try
{
doctest::Context context;
testExitStatus = context.run();
}
catch (exception& e)
{
TEST_FAILED(e.what())
}
catch (...)
{
TEST_FAILED("Caught unknown exception.")
}
TEST_EPILOG
}
#endif |
A few notes on the wandbox example:
Do you have tests with your macros in
there has to be only one runner in a binary (executable or shared object). If you implement the test runner in each test file then that would mean that each test file compiles to a separate binary. In that case the same assert wouldn't be run multiple times, but there will be different executables with different test registries... when mixing tests and production code - the use of When writing tests not in separate test files but in your production code files you could hack your build system to build only those TUs twice - with doctest enabled and disabled, but that would be a complicated setup. But it can be achieved. @chambm I might be missing something when trying to understand your question/problem... |
Yes hacking my build system is basically what I decided was the simplest setup. To make things more concrete, take a look at one of my Jamfiles: You can see that each class has its own unit test driver which gets compiled and run by Boost.Build (
where doctest is defined as:
We have over 200 individual unit test files, so we use TeamCity as the overall test registry. Adding doctests to that registry is no problem (telling TeamCity about the tests is what TEST_PROLOG and TEST_EPILOG do). |
Been thinking about this too. Building the code twice is out of the question in bigger projects which already suffer from insane compilation times thx to the lack of (proper) modules.
This is a good idea already. But that means in release builds we have at least the space overhead plus potential cache effects and all the test registration code running at startup every time even though it will never be used, which may or may not pose a problem idk (has anyone ever measured the overhead?). |
How can I mix production code with the Unit Tests?
Initial page says:
How can I mix production code with the Unit Tests?
I am thinking about just doing the
#include "doctest.h"
, and writing the tests down, but how the Unit Tests code will not end up on the production binary after compiled from the main program, instead of the Unit Tests Driver Class:I found out this macro
DOCTEST_CONFIG_DISABLE
. Therefore how would it work, I would have to define it on every production source code file, to disable the Unit Tests?The text was updated successfully, but these errors were encountered: