Here is a single-header small unit testing framework I wrote, that makes use of mostly preprocessor macros. I made it for knowing how unit testing works and, also, try to depend less of third-party libraries.Code:utest.hpp:#ifndef CYNODELIC_UTEST_UTEST_HPP#define CYNODELIC_UTEST_UTEST_HPP/* Usage: * 1. Initialize the test suite * 2. Add tests * 3. Initialize the running function * 4. Run */#include <iostream>#include <cstdlib>#include <cstdint>#include <cmath>#include <functional>#include <type_traits>#include <limits>#include <string>#include <vector>// Print file and line info (do nothing if CYNODELIC_UTEST_DO_NOT_PRINT_LINE_INFO was defined)#ifndef CYNODELIC_UTEST_DO_NOT_PRINT_LINE_INFO    #define CYNODELIC_UTEST_FILE_LINE_OUT std::cout <<          File:  << __FILE__ <<  on line  << __LINE__ << \n;#else    #define CYNODELIC_UTEST_FILE_LINE_OUT#endif// Initialize test suite#define CYNODELIC_UTEST_INITIALIZE_TEST_SUITE(name) \namespace utest_suite_##name##_ \{                               \    std::vector<cynodelic::utest::test_cont>   test_list; \    std::vector<cynodelic::utest::test_status> stats;     \}                               \// Checking#define CYNODELIC_UTEST_CHECK_TRUE(cnd) \do \{  \    if (!(cnd))                             \    {                                       \        std::cout << cynodelic::utest::internal::co_red([FAILED] )   << #cnd << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                  \        stats.push_back(cynodelic::utest::test_status::FAIL);                           \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #cnd << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                           \    }    \} while (false) \#define CYNODELIC_UTEST_CHECK_FALSE(cnd) CYNODELIC_UTEST_CHECK_TRUE(!(cnd))#define CYNODELIC_UTEST_CHECK_OPERATOR(op,a,b) \do \{  \    if (!((a) op (b))) \    {                  \        std::cout << cynodelic::utest::internal::co_red([FAILED] )   << #a <<   << #op <<   << #b << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                                           \        stats.push_back(cynodelic::utest::test_status::FAIL);                                                    \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #a <<   << #op <<   << #b << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                                                    \    }    \} while (false) \#define CYNODELIC_UTEST_CHECK_EQUALS(val_a,val_b)       CYNODELIC_UTEST_CHECK_OPERATOR(==,val_a,val_b)#define CYNODELIC_UTEST_CHECK_NON_EQUALS(val_a,val_b)   CYNODELIC_UTEST_CHECK_OPERATOR(!=,val_a,val_b)#define CYNODELIC_UTEST_CHECK_GREATER_THAN(val_a,val_b) CYNODELIC_UTEST_CHECK_OPERATOR(>,val_a,val_b)#define CYNODELIC_UTEST_CHECK_LESSER_THAN(val_a,val_b)  CYNODELIC_UTEST_CHECK_OPERATOR(<,val_a,val_b)#define CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(a,b)       \do \{  \    if (!(cynodelic::utest::internal::almost_equals(a,b))) \    {                                                      \        std::cout << cynodelic::utest::internal::co_red([FAILED] )   << #a <<  ==  << #b << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                                \        stats.push_back(cynodelic::utest::test_status::FAIL);                                         \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #a <<  ==  << #b << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                                         \    }    \} while (false) \// Assertion#define CYNODELIC_UTEST_ASSERT_TRUE(cnd) \do \{  \    if (!(cnd))                              \    {                                        \        std::cerr << cynodelic::utest::internal::co_red([FAILED] )   << #cnd << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                  \        std::cout << \n << stats.size() <<  tests have been passed.\n;              \        std::exit(EXIT_FAILURE);                                                        \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #cnd << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                           \    }    \} while (false) \#define CYNODELIC_UTEST_ASSERT_FALSE(cnd) CYNODELIC_UTEST_ASSERT_TRUE(!(cnd))#define CYNODELIC_UTEST_ASSERT_OPERATOR(op,a,b)                                      \do \{  \    if (!((a) op (b)))                                                                   \    {                                                                                    \        std::cerr << cynodelic::utest::internal::co_red([FAILED] )   << #a <<   << #op <<   << #b << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                                           \        std::cout << \n << stats.size() <<  tests have been passed.\n;                                       \        std::exit(EXIT_FAILURE);                                                                                 \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #a <<   << #op <<   << #b << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                                                    \    }    \} while (false) \#define CYNODELIC_UTEST_ASSERT_EQUALS(val_a,val_b)       CYNODELIC_UTEST_ASSERT_OPERATOR(==,val_a,val_b)#define CYNODELIC_UTEST_ASSERT_NON_EQUALS(val_a,val_b)   CYNODELIC_UTEST_ASSERT_OPERATOR(!=,val_a,val_b)#define CYNODELIC_UTEST_ASSERT_GREATER_THAN(val_a,val_b) CYNODELIC_UTEST_ASSERT_OPERATOR(>,val_a,val_b)#define CYNODELIC_UTEST_ASSERT_LESSER_THAN(val_a,val_b)  CYNODELIC_UTEST_ASSERT_OPERATOR(<,val_a,val_b)#define CYNODELIC_UTEST_ASSERT_FLOAT_EQUALS(a,b)      \do \{  \    if (!(cynodelic::utest::internal::almost_equals(a,b))) \    {                                                      \        std::cerr << cynodelic::utest::internal::co_red([FAILED] )   << #a <<  ==  << #b << \n; \        CYNODELIC_UTEST_FILE_LINE_OUT;                                                                \        std::cout << \n << stats.size() <<  tests have been passed.\n;                            \        std::exit(EXIT_FAILURE);                                                                      \    }    \    else \    {    \        std::cout << cynodelic::utest::internal::co_green([ PASS ] ) << #a <<  ==  << #b << \n; \        stats.push_back(cynodelic::utest::test_status::PASS);                                         \    }    \} while (false) \// Add a test#define CYNODELIC_UTEST_ADD_TEST(name,method) \namespace utest_suite_##name##_               \{                                             \    struct utest_##name##_method_##method##_holder \    {  \        utest_##name##_method_##method##_holder() \        { \            test_list.push_back(cynodelic::utest::test_cont( #name , #method ,&test_fun)); \        } \        static void test_fun(); \    }; \}          \utest_suite_##name##_::utest_##name##_method_##method##_holder utest_##name##_method_##method; \void utest_suite_##name##_::utest_##name##_method_##method##_holder::test_fun()                \// Initialize the running function#define CYNODELIC_UTEST_INITIALIZE_RUNNING(name) \namespace utest_suite_##name##_                  \{                                                \    bool run_tests() \    {                \        std::cout << ==================== Test suite:  << #name <<  ====================\n\n; \                                                     \        for (std::size_t i=0;i<test_list.size();i++) \        {                                            \            std::cout << Running  << test_list[i].test_method              \                      <<  test from  << test_list[i].test_name << ...\n; \            test_list[i]();                                                  \            std::cout << \n;                                               \        }                                        \                                                 \        std::size_t passed_tests = 0;            \        for (std::size_t i=0;i<stats.size();i++) \            if (stats[i] == cynodelic::utest::test_status::PASS) \                passed_tests++;                              \                                                 \        std::cout << passed_tests <<  out of  << stats.size() <<  tests have been passed.\n\n; \                                                 \        if (passed_tests != stats.size()) \            return false;             \        return true; \    } \} \// Run the tests#define CYNODELIC_UTEST_RUN_TEST_SUITE(name) utest_suite_##name##_::run_tests()namespace cynodelic { namespace utest {namespace internal{// Check equality with doubletemplate <typename FloatTypeA,typename FloatTypeB>inline typename std::enable_if<        std::is_arithmetic<FloatTypeA>::value && std::is_arithmetic<FloatTypeB>::value,        bool    >::type almost_equals(const FloatTypeA& a,const FloatTypeB& b){    double a_ = static_cast<double>(a);    double b_ = static_cast<double>(b);    return std::fabs(a_-b_) < std::numeric_limits<double>::epsilon() || std::fabs(a_-b_) < std::numeric_limits<double>::min();}// Colorize output (if CYNODELIC_UTEST_NON_COLORIZED_OUTPUT is not defined)#ifndef CYNODELIC_UTEST_NON_COLORIZED_OUTPUTinline std::string co_red(const char *x)   { return std::string(\033[1;31m)+std::string(x)+std::string(\033[0m); }inline std::string co_green(const char *x) { return std::string(\033[1;32m)+std::string(x)+std::string(\033[0m); }#elseinline std::string co_red(const char *x)   { return std::string(x); }inline std::string co_green(const char *x) { return std::string(x); }#endif} // end of internal namespace// Test status enum (this is used instead of bool)enum class test_status : std::uint8_t{    FAIL = 0xe0,    PASS = 0xe1};// Test containerstruct test_cont{    std::string test_name;      // Test name    std::string test_method;    // Test method name    std::function<void()> tmet; // The test method    test_cont(const char *name,const char *method,void(*tm_ptr)()) :        test_name(name),        test_method(method),        tmet(tm_ptr) {}    // Run the test contained    void operator()()    {        tmet();    }};}} // end of cynodelic::utest namespace#endifutest_demo.cpp:#include <iostream>#include <cstdlib>#include <utility>#include utest.hpp// This class was made for demonstrationclass vec3{    public:        double x, y, z;        vec3() : x(0), y(0), z(0) {}        vec3(double x,double y,double z) : x(x), y(y), z(z) {}        vec3(const vec3& other) : x(other.x), y(other.y), z(other.z) {}        vec3& operator=(const vec3& other)        {            x = other.x;            y = other.y;            z = other.z;            return *this;        }        void swap(vec3& other)        {            std::swap(x,other.x);            std::swap(y,other.y);            std::swap(z,other.z);        }        friend void swap(vec3& lhs,vec3& rhs)        {            lhs.swap(rhs);        }        friend bool operator==(const vec3& lhs,const vec3& rhs)        {            return (lhs.x == rhs.x)&&(lhs.y == rhs.y)&&(lhs.z == rhs.z);        }        friend bool operator!=(const vec3& lhs,const vec3& rhs)        {            return !(lhs == rhs);        }        vec3& operator+=(const vec3& other)        {            x += other.x;            y += other.y;            z += other.z;            return *this;        }        friend vec3 operator+(vec3 lhs,const vec3& rhs)        {            lhs += rhs;            return lhs;        }        vec3& operator-=(const vec3& other)        {            x -= other.x;            y -= other.y;            z -= other.z;            return *this;        }        friend vec3 operator-(vec3 lhs,const vec3& rhs)        {            lhs -= rhs;            return lhs;        }};CYNODELIC_UTEST_INITIALIZE_TEST_SUITE(vec3);CYNODELIC_UTEST_ADD_TEST(vec3,copy_construction){    vec3 t1(0.5,0.25,0.125);    vec3 t2(t1);    CYNODELIC_UTEST_CHECK_EQUALS(t1,t2);    CYNODELIC_UTEST_CHECK_EQUALS(t1.x,t2.x);    CYNODELIC_UTEST_CHECK_EQUALS(t1.y,t2.y);    CYNODELIC_UTEST_CHECK_EQUALS(t1.z,t2.z);}CYNODELIC_UTEST_ADD_TEST(vec3,assignment){    vec3 t1(1,2,3);    vec3 t2(0.1,0,-0.1);    t1 = t2;    CYNODELIC_UTEST_CHECK_EQUALS(t1,t2);    CYNODELIC_UTEST_CHECK_EQUALS(t1.x,t2.x);    CYNODELIC_UTEST_CHECK_EQUALS(t1.y,t2.y);    CYNODELIC_UTEST_CHECK_EQUALS(t1.z,t2.z);    CYNODELIC_UTEST_CHECK_EQUALS(t1.x,0.1);    CYNODELIC_UTEST_CHECK_EQUALS(t1.y,0);    CYNODELIC_UTEST_CHECK_EQUALS(t1.z,-0.1);}CYNODELIC_UTEST_ADD_TEST(vec3,equality){    vec3 v1(1.111,2.345,3.0005);    vec3 v2(1.111,2.345,3.0005);    CYNODELIC_UTEST_CHECK_TRUE(v1 == v2);    CYNODELIC_UTEST_CHECK_TRUE(v1.x == v2.x);    CYNODELIC_UTEST_CHECK_TRUE(v1.y == v2.y);    CYNODELIC_UTEST_CHECK_TRUE(v1.z == v2.z);    CYNODELIC_UTEST_CHECK_NON_EQUALS(v1,v2);}CYNODELIC_UTEST_ADD_TEST(vec3,inequality){    vec3 v1(0.01,0.1,1);    vec3 v2(2,4,8);    CYNODELIC_UTEST_CHECK_TRUE(v1 != v2);    CYNODELIC_UTEST_CHECK_TRUE(v1.x != v2.x);    CYNODELIC_UTEST_CHECK_TRUE(v1.y != v2.y);    CYNODELIC_UTEST_CHECK_TRUE(v1.z != v2.z);}CYNODELIC_UTEST_ADD_TEST(vec3,swapping){    vec3 v1(-1.1,-1.2,2.1);    vec3 v2(14.32,23.14,32.41);    swap(v1,v2);    CYNODELIC_UTEST_CHECK_EQUALS(v1.x,14.32);    CYNODELIC_UTEST_CHECK_EQUALS(v1.y,23.14);    CYNODELIC_UTEST_CHECK_EQUALS(v1.z,32.41);    CYNODELIC_UTEST_CHECK_EQUALS(v2.x,-1.1);    CYNODELIC_UTEST_CHECK_EQUALS(v2.y,-1.2);    CYNODELIC_UTEST_CHECK_EQUALS(v2.z,2.1);    CYNODELIC_UTEST_CHECK_NON_EQUALS(v1,v2);    CYNODELIC_UTEST_CHECK_NON_EQUALS(v1.x,v2.x);    CYNODELIC_UTEST_CHECK_NON_EQUALS(v1.y,v2.y);    CYNODELIC_UTEST_CHECK_NON_EQUALS(v1.z,v2.z);}CYNODELIC_UTEST_ADD_TEST(vec3,addition){    vec3 v1(1,2,3);    vec3 v2(5,6,7);    vec3 res = v1+v2;    CYNODELIC_UTEST_CHECK_EQUALS(v1+v2,res);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.x+v2.x,res.x);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.y+v2.y,res.y);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.z+v2.z,res.z);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.x+v2.x,6);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.y+v2.y,8);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.z+v2.z,10);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.x,6);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.y,8);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.z,10);}CYNODELIC_UTEST_ADD_TEST(vec3,substraction){    vec3 v1(1.25,0.75,4.101001);    vec3 v2(0.5 ,2.25,4);    vec3 res = v1-v2;    CYNODELIC_UTEST_CHECK_EQUALS(v1-v2,res);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.x-v2.x,res.x);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.y-v2.y,res.y);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.z-v2.z,res.z);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.x-v2.x,0.75);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.y-v2.y,-1.5);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(v1.z-v2.z,0.101001);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.x,0.75);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.y,-1.5);    CYNODELIC_UTEST_CHECK_FLOAT_EQUALS(res.z,0.101001);}// Initialize the test suite running functionCYNODELIC_UTEST_INITIALIZE_RUNNING(vec3);int main(int argc,char **argv){    // Check if all the tests passed or failed    bool test_res = CYNODELIC_UTEST_RUN_TEST_SUITE(vec3);    if (!test_res)        std::cout << Test has been failed.\n;    else        std::cout << Test finished successfully.\n;}Any suggestions, improvements, etc. are welcome, especially regarding its design.
Small unit testing framework
c++;unit testing;reinventing the wheel;c++14