To start using the fcfTest library, you first need to integrate it into your build system. Since fcfTest is a header-only library, the integration process is straightforward, but it requires a proper definition of the implementation macro to generate the library's logic.
Installation Methods
Depending on your project's workflow, you can choose one of the following three methods to include fcfTest.
1. Manual Installation (Git Clone)
This is the simplest method if you want full control over the library source or if you are not using a package manager. You can simply clone the repository and point your build system to the include directory.
Steps:
- Clone the repository:
> cd PROJECT_DIRECTORY/include
> git clone https://github.com/fcf-framework/fcfTest.git
-
Add the
include directory of the cloned repository to your project's include paths.
Example CMakeLists.txt for manual setup:
cmake_minimum_required(VERSION 3.15...${CMAKE_VERSION})
project(FcfTestExample CXX)
# Specify the path where you downloaded fcfTest
set(FCFTEST_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
add_executable(my_tests main.cpp)
# Add the directory to include paths
target_include_directories(my_tests PRIVATE ${FCFTEST_INCLUDE_DIR})
-
Create the main.cpp file:
#define FCF_TEST_IMPLEMENTATION
#include <fcfTest/test.hpp>
FCF_TEST_DEFINE("Core", "Math", "AdditionTest") {
FCF_TEST(2 + 2 == 4);
}
int main(int a_argc, char* a_argv[]) {
bool error;
fcf::NTest::cmdRun(a_argc, a_argv, fcf::NTest::CRM_RUN, &error);
return error ? 1 : 0;
}
-
Now all that remains is to build the project and run it.
> mkdir PROJECT_DIRECTORY/build
> cd PROJECT_DIRECTORY/build
> cmake ..
> cmake --build .
# And launch our app
> ./my_tests
Terminal output:
Performing the test: "Core" -> "Math" -> "AdditionTest" ...
[SUCCESS] Test completed successfully (0.000`000`536 sec)
[SUCCESS] All tests were completed.
Tests: 1 passed, 0 failed, 0 skipped, 1 total
Duration: 0.000`000`536 sec
2. Using CMake FetchContent
For modern CMake projects, FetchContent is the recommended way. It automatically downloads the library during the configuration step, ensuring that everyone working on the project uses the same version.
Create a CMakeLists.txt file with the following content:
cmake_minimum_required(VERSION 3.15...${CMAKE_VERSION})
project(FcfTestExampleWithFetchContent CXX)
# Include the standard CMake module 'FetchContent' to manage external dependencies.
include(FetchContent)
# Declare the external dependency 'fcfTest'.
# This specifies where to download the library and which branch/tag to target.
FetchContent_Declare(
fcfTest
GIT_REPOSITORY https://github.com/fcf-framework/fcfTest.git
GIT_TAG main # Targets the 'main' branch (can be changed to a specific tag like v1.2.8)
)
# Download, configure, and make the 'fcfTest' dependency available to the project immediately.
FetchContent_MakeAvailable(fcfTest)
add_executable(my_tests main.cpp)
# Links the fcfTest target to our executable.
# This automatically handles include paths, compile definitions, and any options required by the library.
target_link_libraries(my_tests PRIVATE fcf::fcfTest)
The remaining steps are similar to the previous part.
3. Using vcpkg
If you are already using vcpkg as your package manager, you can install fcfTest with a single command. The library is registered under the name fcftest.
First, add it to your vcpkg.json:
{
"name": "my-tests",
"version": "1.0.0",
"dependencies": [
"fcftest"
]
}
Then, in your CMakeLists.txt, use find_package:
cmake_minimum_required(VERSION 3.15...${CMAKE_VERSION})
project(FcfTestExampleWithVcpkg CXX)
# Locate the external 'fcfTest' package using CMake's CONFIG mode.
# The REQUIRED keyword guarantees that the configuration process will stop and throw an error
# if the package (installed via vcpkg) cannot be found.
find_package(fcfTest CONFIG REQUIRED)
# Create an executable target named 'my_tests' compiled from the 'main.cpp' source file.
add_executable(my_tests main.cpp)
# Links the fcfTest target to our executable.
# This automatically handles include paths, compile definitions, and any options required by the library.
target_link_libraries(my_tests PRIVATE fcf::fcfTest)
Next, create the application source file `main.cpp`; for example, you can use the code from the first section of this article.
Now, all that remains is to execute the commands to build the project.
# Set the CMAKE_TOOLCHAIN_FILE environment variable
# So that CMake can find the installed libraries
> export VCPKG_ROOT="${HOME}/Projects/vcpkg"
> export CMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
# Create a build directory and enter it
> mkdir PROJECT_DIRECTORY/build
> cd PROJECT_DIRECTORY/build
# Configure the project
> cmake ..
# Build the project
> cmake --build .
# Run the application
> ./my_tests
The FCF_TEST_IMPLEMENTATION Macro
Because fcfTest is a header-only library, it needs to know where to generate its internal logic (the implementation). This is controlled by the FCF_TEST_IMPLEMENTATION macro. You must define this macro in exactly one translation unit (one .cpp file) before including the main header.
Scenario 1: Implementation in a Single File
This is the most common approach for small projects or simple test runners. You define the macro in your main.cpp file.
// --- main.cpp ---
// The macro MUST be defined before the include
#define FCF_TEST_IMPLEMENTATION
#include <fcfTest/test.hpp>
// Define a simple test
FCF_TEST_DEFINE("Demo", "Math", "Addition") {
FCF_TEST(1 + 1 == 2);
}
int main(int argc, char* argv[]) {
bool error = false;
// Run the tests
fcf::NTest::cmdRun(argc, argv, fcf::NTest::CRM_RUN, &error);
return error ? 1 : 0;
}
Scenario 2: Implementation in a Separate Library (DLL/Static)
In large-scale professional projects, you might want to compile the test framework logic into a separate library (e.g., a DLL or a static library) to speed up compilation times. In this case, the macro is defined in the library's source code, and the application simply includes the header without the macro.
This approach prevents the heavy implementation logic from being recompiled every time you change a test in your main application.
Step 1: Create the library source (e.g., fcf_impl.cpp)
// --- fcf_impl.cpp ---
// This file is compiled into your library (libfcftest_impl.dll/a)
// Define the macro to include the library implementation code
#define FCF_TEST_IMPLEMENTATION
// Define the function export macro so that our library exports them
#define FCF_TEST_EXPORT
#include <fcfTest/test.hpp>
// You can even define some global fixtures here if needed
FCF_TEST_BEFORE_DEFINE("*", "*", "*", fcf::NTest::FL_GLOBAL) {
fcf::NTest::log() << "Library implementation initialized." << std::endl;
}
Step 2: Use the library in your application (main.cpp)
// --- main.cpp ---
// IMPORTANT: Do NOT define FCF_TEST_IMPLEMENTATION here!
// The implementation is already provided by the linked library.
// Define the import macro FCF_TEST_IMPORT
// so that our application pulls the fcfTest functions from the library.
#define FCF_TEST_IMPORT
#include <fcfTest/test.hpp>
FCF_TEST_DEFINE("App", "Logic", "Test1") {
FCF_TEST(true);
}
int main(int argc, char* argv[]) {
bool error = false;
// The runner will find the implementation in the linked library
fcf::NTest::cmdRun(argc, argv, fcf::NTest::CRM_RUN, &error);
return error ? 1 : 0;
}
Step 3: Create a CMake file for the build rules for our project. (CMakeLists.txt)
cmake_minimum_required(VERSION 3.15...${CMAKE_VERSION})
project(FcfTestProject)
# Locate the external 'fcfTest' package using CMake's CONFIG mode.
# The REQUIRED keyword guarantees that the configuration process will stop and throw an error
# if the package (installed via vcpkg) cannot be found.
find_package(fcfTest CONFIG REQUIRED)
# 1. Create a dynamic shared library
add_library(libfcftest_impl SHARED fcf_impl.cpp)
# Adds paths to the library's header files so that #include directives work correctly.
# The 'PRIVATE' keyword ensures that these paths are used exclusively within 'my_tests'.
target_link_libraries(libfcftest_impl PRIVATE fcf::fcfTest)
# This is an example of how to declare a macro.
# We previously declared this macro in the .cpp file, but it is better practice to declare it here.
# Two approaches are shown here for educational purposes.
# Declare macros for building the dynamic library:
# - FCF_TEST_EXPORT: instructs the framework to export symbols from the DLL/SO file
#
# target_compile_definitions(libfcftest_impl PRIVATE
# FCF_TEST_EXPORT
# )
# 2. Create the application executable
add_executable(my_tests main.cpp)
target_link_libraries(my_tests PRIVATE fcf::fcfTest)
# Link the application with the created library
target_link_libraries(my_tests PRIVATE libfcftest_impl)
# This is an example of how to declare a macro.
# We previously declared this macro in the .cpp file, but it is better practice to declare it here.
# Two approaches are shown here for educational purposes. # Define a macro for the application:
# - FCF_TEST_IMPORT: indicates that test functions and variables are imported from an external dynamic library
#
# target_compile_definitions(my_tests PRIVATE
# FCF_TEST_IMPORT
# )
Step 4. Now, let's execute the commands in the terminal to configure the build and build our application.
# This time, copy the fcfTest repository into our project's include directory
> mkdir PROJECT_DIRECTORY/..
> cd PROJECT_DIRECTORY/..
> git clone https://github.com/fcf-framework/fcfTest.git
> cd PROJECT_DIRECTORY
# Create the build directory
mkdir PROJECT_DIRECTORY/build
> cd PROJECT_DIRECTORY/build
# Configure the project.
# Pass the directory containing our included libraries (fcfTest) to CMAKE_PREFIX_PATH.
# This is necessary for the find_package() function to locate our library.
> cmake .. -DCMAKE_PREFIX_PATH=PROJECT_DIRECTORY/..
# Build the project
> cmake --build .
# Check the files generated by the build system
> ls
CMakeCache.txt CMakeFiles cmake_install.cmake liblibfcftest_impl.so Makefile my_tests
# Now, run the application
> ./my_tests
The application output is shown below:
> Library implementation initialized.
Performing the test: "App" -> "Logic" -> "Test1" ...
[SUCCESS] Test completed successfully (0.000`002`328 sec)
[SUCCESS] All tests were completed.
Tests: 1 passed, 0 failed, 0 skipped, 1 total
Duration: 0.000`002`328 sec