Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 非测试代码中的gtest断言_C++_Unit Testing_Assert_Googletest - Fatal编程技术网

C++ 非测试代码中的gtest断言

C++ 非测试代码中的gtest断言,c++,unit-testing,assert,googletest,C++,Unit Testing,Assert,Googletest,比如: if(gtest::is_running) ASSERT_TRUE(...); else assert(...); 我怎样才能做到这一点呢?即使这在技术上是可能的(我认为不是),我真的不认为让生产代码依赖于测试框架是一个好主意 主要原因是健壮性、关注点分离和解耦:在生产代码中引入特定于测试的条件会使代码不必要地难以理解,并且可能会降低测试套件的可信度(毕竟,您的测试不会强调生产代码将经过的完全相同的路径) 此外,有一天您可能希望更改测试环境中的某些内容(例如,单元测试框

比如:

if(gtest::is_running)
    ASSERT_TRUE(...);
else
    assert(...);

我怎样才能做到这一点呢?

即使这在技术上是可能的(我认为不是),我真的不认为让生产代码依赖于测试框架是一个好主意

主要原因是健壮性、关注点分离和解耦:在生产代码中引入特定于测试的条件会使代码不必要地难以理解,并且可能会降低测试套件的可信度(毕竟,您的测试不会强调生产代码将经过的完全相同的路径)

此外,有一天您可能希望更改测试环境中的某些内容(例如,单元测试框架的版本,或单元测试框架本身),这种依赖性可能会迫使您相应地修改生产代码,从而有引入新错误的风险

如果您想要验证的是,当客户端违反函数的前提条件时,您的断言实际上会触发(即,如果您想要测试前提条件是否由您的断言正确验证),那么可能与您以及激发它的库相关

<>如果这不是你的项目可行的技术,也许你可以考虑采用基于依赖反转的策略。最简单的方法是:

  • 使用抽象成员函数
    verify()
    使用
    bool
    定义一个抽象类
    Verifier
  • 从它派生一个
    AssertingVerifier
    类(用于生产代码),该类重写
    verify()
    ,并将其参数转发到
    assert()
    Verifier
    AssertVerifier
    都将存在于您的生产代码中
  • 在单元测试项目中,定义第二个派生类,
    GracefulTestVerifier
    ,该派生类重写
    verify()
    ,并将其参数转发给
    ASSERT\u TRUE()
    ——或者执行您认为最合适的操作
  • 找出将
    验证器
    注入生产代码的最佳方法-存在多种可能性,但要知道哪种方法最适合,需要对您的设计有详细的了解。然后在常规执行环境中注入
    AssertVerifier
    ,在测试环境中注入
    GracefulTestVerifier

  • 这样,执行可能从生产代码流向测试框架,而生产代码在物理上不依赖于测试框架本身。

    即使这在技术上是可能的(我不认为是),我真的不认为让生产代码依赖于测试框架是一个好主意

    主要原因是健壮性、关注点分离和解耦:在生产代码中引入特定于测试的条件会使代码不必要地难以理解,并且可能会降低测试套件的可信度(毕竟,您的测试不会强调生产代码将经过的完全相同的路径)

    此外,有一天您可能希望更改测试环境中的某些内容(例如,单元测试框架的版本,或单元测试框架本身),这种依赖性可能会迫使您相应地修改生产代码,从而有引入新错误的风险

    如果您想要验证的是,当客户端违反函数的前提条件时,您的断言实际上会触发(即,如果您想要测试前提条件是否由您的断言正确验证),那么可能与您以及激发它的库相关

    <>如果这不是你的项目可行的技术,也许你可以考虑采用基于依赖反转的策略。最简单的方法是:

  • 使用抽象成员函数
    verify()
    使用
    bool
    定义一个抽象类
    Verifier
  • 从它派生一个
    AssertingVerifier
    类(用于生产代码),该类重写
    verify()
    ,并将其参数转发到
    assert()
    Verifier
    AssertVerifier
    都将存在于您的生产代码中
  • 在单元测试项目中,定义第二个派生类,
    GracefulTestVerifier
    ,该派生类重写
    verify()
    ,并将其参数转发给
    ASSERT\u TRUE()
    ——或者执行您认为最合适的操作
  • 找出将
    验证器
    注入生产代码的最佳方法-存在多种可能性,但要知道哪种方法最适合,需要对您的设计有详细的了解。然后在常规执行环境中注入
    AssertVerifier
    ,在测试环境中注入
    GracefulTestVerifier

  • 这样,执行可能从生产代码流向测试框架,而您的生产代码在物理上不依赖于测试框架本身。

    从另一个方向来实现这一点怎么样?更改断言的行为,而不是更改gtest行为

    例如,提供了一个
    BOOST_ASSERT
    宏,默认情况下,该宏的行为与
    ASSERT
    相同。但是,如果定义了
    BOOST\u ENABLE\u ASSERT\u处理程序
    ,则它将查找一个
    ::BOOST::assertion\u failed
    函数,您必须提供该函数。您可以将库代码设计为在测试套件外部使用标准断言行为构建,并在测试套件内部使用调用gtest的
    FAIL()
    :boost::assertion\u failed

    如果你不想使用Boost,它会
    #ifdef GTEST_ON
        ASSERT_TRUE(...);
    #else
        assert(...);
    #endif
    
    #pragma once
    
    #include "debug.hpp"
    
    #ifdef UNIT_TESTS
    #include <gtest/gtest.h>
    #endif
    
    #include <cassert>
    
    #define ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp, precondition) \
        if (!(precondition)); else if(!!(exp)); else ::utility::debug_break()
    
    #ifdef GTEST_FAIL
    
    #ifdef _MSC_VER
        #if _MSC_VER < 1600 // < MSVC++ 10 (Visual Studio 2010)
            #error lambda is not supported
        #endif
    #else
        #if __cplusplus < 201103L
            #error lambda is not supported
        #endif
    #endif
    
    // TIPS:
    //  * all lambdas captured by reference because of the error in the MSVC 2015:
    //    `error C3493 : '...' cannot be implicitly captured because no default capture mode has been specified`
    //  * if debugger is attached but `::testing::GTEST_FLAG(break_on_failure)` has not been setted,
    //    then an assertion does a post break.
    
    // gtest asserts rebind with the `void` error workaround (C++11 and higher is required)
    #undef ASSERT_TRUE
    #define ASSERT_TRUE(condition) [&]() -> void { \
            const bool is_success = ::utility::is_true(condition); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_TEST_BOOLEAN_(is_success, #condition, false, true, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_TEST_BOOLEAN_(is_success, #condition, false, true, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(is_success, !break_on_failure); \
        }()
    #undef ASSERT_FALSE
    #define ASSERT_FALSE(condition) [&]() -> void { \
            const bool is_success = ::utility::is_false(condition); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_TEST_BOOLEAN_(is_success, #condition, true, false, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_TEST_BOOLEAN_(is_success, #condition, true, false, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(is_success, !break_on_failure); \
        }()
    
    #if !GTEST_DONT_DEFINE_ASSERT_EQ
    #undef ASSERT_EQ
    #define ASSERT_EQ(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #if !GTEST_DONT_DEFINE_ASSERT_NE
    #undef ASSERT_NE
    #define ASSERT_NE(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperNE(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #if !GTEST_DONT_DEFINE_ASSERT_LE
    #undef ASSERT_LE
    #define ASSERT_LE(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLE(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #if !GTEST_DONT_DEFINE_ASSERT_LT
    #undef ASSERT_LT
    #define ASSERT_LT(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLT(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #if !GTEST_DONT_DEFINE_ASSERT_GE
    #undef ASSERT_GE
    #define ASSERT_GE(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGE(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #if !GTEST_DONT_DEFINE_ASSERT_GT
    #undef ASSERT_GT
    #define ASSERT_GT(val1, val2) [&]() -> void { \
            const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGT(#val1, #val2, val1, val2); \
            const bool break_on_failure = ::testing::GTEST_FLAG(break_on_failure); \
            if (break_on_failure) { \
                GTEST_ASSERT_(exp_value, GTEST_FATAL_FAILURE_); \
            } else { \
                GTEST_ASSERT_(exp_value, GTEST_NONFATAL_FAILURE_); \
            } \
            ASSERT_FAIL_BREAK_ON_ATTACHED_DEBUGGER(exp_value, !break_on_failure); \
        }()
    #endif
    
    #define ASSERT(x) ASSERT_TRUE(x)
    
    #else
    
    #ifndef ASSERT_IMPL
    #define ASSERT_IMPL(exp) assert(exp)
    #endif
    
    #ifdef _DEBUG
    
    #define ASSERT_TRUE(exp) ASSERT_IMPL(exp)
    #define ASSERT_FALSE(exp) ASSERT_IMPL(!(exp))
    
    #define ASSERT_EQ(v1, v2) ASSERT_IMPL((v1) == (v2))
    #define ASSERT_NE(v1, v2) ASSERT_IMPL((v1) != (v2)))
    #define ASSERT_LE(v1, v2) ASSERT_IMPL((v1) <= (v2))
    #define ASSERT_LT(v1, v2) ASSERT_IMPL((v1) < (v2))
    #define ASSERT_GE(v1, v2) ASSERT_IMPL((v1) >= (v2))
    #define ASSERT_GT(v1, v2) ASSERT_IMPL((v1) > (v2))
    
    #define ASSERT(exp) ASSERT_IMPL(exp)
    
    #else
    
    #define ASSERT_TRUE(exp) (::utility::is_true(exp), (void)0)
    #define ASSERT_FALSE(exp) (::utility::is_false(exp), (void)0))
    
    #define ASSERT_EQ(v1, v2) (::utility::is_equal(v1, v2), (void)0)
    #define ASSERT_NE(v1, v2) (::utility::is_not_equal(v1, v2), (void)0)
    #define ASSERT_LE(v1, v2) (::utility::is_less_or_equal(v1, v2), (void)0)
    #define ASSERT_LT(v1, v2) (::utility::is_less(v1, v2), (void)0)
    #define ASSERT_GE(v1, v2) (::utility::is_greater_or_equal(v1, v2), (void)0)
    #define ASSERT_GT(v1, v2) (::utility::is_greater(v1, v2), (void)0)
    
    #define ASSERT(exp) ::utility::is_true(exp)
    
    #endif
    
    #endif
    
    namespace utility
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T>
        inline bool is_true(const T & v)
        {
            return !!v; // to avoid warnings of truncation to bool
        }
    
        template<typename T>
        inline bool is_false(const T & v)
        {
            return !v; // to avoid warnings of truncation to bool
        }
    
        template<typename T1, typename T2>
        inline bool is_equal(const T1 & v1, const T2 & v2)
        {
            return v1 == v2;
        }
    
        template<typename T1, typename T2>
        inline bool is_not_equal(const T1 & v1, const T2 & v2)
        {
            return v1 != v2;
        }
    
        template<typename T1, typename T2>
        inline bool is_less_or_equal(const T1 & v1, const T2 & v2)
        {
            return v1 <= v2;
        }
    
        template<typename T1, typename T2>
        inline bool is_less(const T1 & v1, const T2 & v2)
        {
            return v1 < v2;
        }
    
        template<typename T1, typename T2>
        inline bool is_greater_or_equal(const T1 & v1, const T2 & v2)
        {
            return v1 >= v2;
        }
    
        template<typename T1, typename T2>
        inline bool is_greater(const T1 & v1, const T2 & v2)
        {
            return v1 > v2;
        }
    }
    
    #pragma once
    
    namespace utility
    {
        void debug_break(bool breakCondition = true);
        bool is_under_debugger();
    }
    
    #include "debug.hpp"
    #include "platform.hpp"
    
    #if defined(UTILITY_PLATFORM_WINDOWS)
    #include <windows.h>
    #include <intrin.h>
    #elif defined(UTILITY_PLATFORM_POSIX)
    #include <sys/ptrace.h>
    #include <signal.h>
    static void signal_handler(int) { }
    #else
    #error is_under_debugger is not supported for this platform
    #endif
    
    
    namespace utility {
    
    void debug_break(bool breakCondition)
    {
        // avoid signal if not under debugger
        if (breakCondition && is_under_debugger()) {
    #if defined(UTILITY_COMPILER_CXX_MSC)
            __debugbreak(); // won't require debug symbols to show the call stack, when the DebugBreak() will require system debug symbols to show the call stack correctly
    #elif defined(UTILITY_PLATFORM_POSIX)
            signal(SIGTRAP, signal_handler);
    #else
    #error debug_break is not supported for this platform
    #endif
        }
    }
    
    bool is_under_debugger()
    {
    #if defined(UTILITY_PLATFORM_WINDOWS)
        return !!::IsDebuggerPresent();
    #elif defined(UTILITY_PLATFORM_POSIX)
        return ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1;
    #endif
    }
    
    }
    
    #pragma once
    
    // linux, also other platforms (Hurd etc) that use GLIBC, should these really have their own config headers though?
    #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
    #  define UTILITY_PLATFORM_LINUX
    #  define UTILITY_PLATFORM_POSIX
    #  if defined(__mcbc__)
    #     define UTILITY_PLATFORM_MCBC
    #     define UTILITY_PLATFORM_SHORT_NAME "MCBC"
    #  elif defined( __astra_linux__ )
    #     define UTILITY_PLATFORM_ASTRA_LINUX
    #     define UTILITY_PLATFORM_SHORT_NAME "Astra Linux"
    #  else
    #     define UTILITY_PLATFORM_SHORT_NAME "Linux"
    #  endif
    #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) // BSD:
    #  define UTILITY_PLATFORM_BSD
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "BSD"
    #elif defined(sun) || defined(__sun) // solaris:
    #  define UTILITY_PLATFORM_SOLARIS
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "Solaris"
    #elif defined(__CYGWIN__) // cygwin is not win32:
    #  define UTILITY_PLATFORM_CYGWIN
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "Cygwin"
    #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) // win32:
    #  define UTILITY_PLATFORM_WINDOWS
    #  define UTILITY_PLATFORM_SHORT_NAME "Windows"
    #  if defined(__MINGW32__)  //  Get the information about the MinGW runtime, i.e. __MINGW32_*VERSION.
    #     include <_mingw.h>
    #  endif
    #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) // MacOS
    #  define UTILITY_PLATFORM_APPLE
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "MacOS"
    #elif defined(__QNXNTO__)  // QNX:
    #  define UTILITY_PLATFORM_QNIX
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "QNX"
    #elif defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)
    #  define UTILITY_PLATFORM_UNIX
    #  define UTILITY_PLATFORM_POSIX
    #  define UTILITY_PLATFORM_SHORT_NAME "Unix"
    #else
    #   error Unknown platform
    #endif
    
    #if defined(__GNUC__)
    #   define UTILITY_COMPILER_CXX_GCC
    #   define UTILITY_COMPILER_CXX "gcc"
    #   define UTILITY_COMPILER_CXX_VERSION __GNUC__
    #   if __GNUC__ < 4
    #     error "Unsuported gcc version"
    #   endif
    #elif defined(_MSC_VER)
    #   define UTILITY_COMPILER_CXX_MSC
    #   define UTILITY_COMPILER_CXX "MS VisualC"
    #   define UTILITY_COMPILER_CXX_VERSION _MSC_VER
    #else
    #   error "Unknown compiler"
    #endif