【问题标题】:How to start working with GTest and CMake如何开始使用 GTest 和 CMake
【发布时间】:2012-01-20 10:03:15
【问题描述】:

我最近被推荐使用 CMake 来编译我的 C++ 项目,现在想开始为我的代码编写一些单元测试。我已决定使用 Google 测试实用程序来帮助解决此问题,但需要一些帮助才能开始。

我整天都在阅读各种指南和示例,包括 Primerintroduction at IBM 和一些关于 SO(herehere)的问题以及我忘记的其他来源。我意识到那里有很多,但不知何故我仍然遇到困难。

我目前正在尝试实施最基本的测试,以确认我已正确编译/安装 gtest 并且它无法正常工作。唯一的源文件(testgtest.cpp)几乎完全取自this之前的答案:

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)
{
    EXPECT_EQ(1, 1);
}

和我关联的CMakeLists.txt如下:

cmake_minimum_required(VERSION 2.6)
project(basic_test)

# Setup testing
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIR})

# Add test cpp file
add_executable(runUnitTests
    testgtest.cpp
)

# Link test executable against gtest & gtest_main
target_link_libraries(runUnitTests ${GTEST_LIBRARY_DEBUG} ${GTEST_MAIN_LIBRARY_DEBUG})

add_test(
    NAME runUnitTests
    COMMAND runUnitTests
)

请注意,我选择链接 gtest_main 而不是在 cpp 文件末尾提供 main,因为我相信这将使我能够更轻松地将测试扩展到多个文件。

在构建生成的 .sln 文件时(在 Visual C++ 2010 Express 中),不幸的是,我得到了一长串表单错误

2>msvcprtd.lib(MSVCP100D.dll) : error LNK2005: "public: virtual __thiscall std::basic_iostream<char,struct std::char_traits<char> >::~basic_iostream<char,struct std::char_traits<char> >(void)" (??1?$basic_iostream@DU?$char_traits@D@std@@@std@@UAE@XZ) already defined in gtestd.lib(gtest-all.obj)

我认为这意味着我没有成功链接到 gtest 库。我确保在链接调试库时,我尝试在调试模式下构建。

编辑

在进行了更多挖掘之后,我认为我的问题与我正在构建 gtest 的库类型有关。使用 CMake 构建 gtest 时,如果未选中 BUILD_SHARED_LIBS,并且我将我的程序与这些 .lib 文件链接起来,则会出现上述错误。但是,如果选中BUILD_SHARED_LIBS,那么我会生成一组 .lib 和 .dll 文件。现在链接这些 .lib 文件时,程序会编译,但运行时会抱怨找不到 gtest.dll。

SHARED 和非SHARED 库之间有什么区别,如果我选择不共享,为什么它不起作用?我的项目的 CMakeLists.txt 中是否有我缺少的选项?

【问题讨论】:

  • 您可以通过使用ExternalProject_Add 而不是add_subdirectory 来避免在自己的文件中包含GTest 源。详情请见this answer
  • 为什么我们可以在上面的解决方案示例中访问 ${gtest_SOURCE_DIR}?该变量是如何/在哪里声明的?
  • 哦,它是在 gtest-1.6.0/CMakeLists.txt 中声明的:"project(gtest CXX C)" 这使得变量 gtest_SOURCE_DIR 和 gtest_BINARY_DIR 可用。
  • enable_testing() 是做什么的?
  • @updogliu:它启用 ctest 和“测试”(或“RUN_TESTS”)目标。它与 add_test() cmake 命令一起使用。

标签: unit-testing cmake googletest


【解决方案1】:

解决方案是将 gtest 源目录作为项目的子目录。如果它对任何人都有帮助,我已经包含了下面的工作 CMakeLists.txt。

cmake_minimum_required(VERSION 2.6)
project(basic_test)

################################
# GTest
################################
ADD_SUBDIRECTORY (gtest-1.6.0)
enable_testing()
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})

################################
# Unit Tests
################################
# Add test cpp file
add_executable( runUnitTests testgtest.cpp )
# Link test executable against gtest & gtest_main
target_link_libraries(runUnitTests gtest gtest_main)
add_test( runUnitTests runUnitTests )

【讨论】:

  • 我不确定 add_test() 是做什么的,但它似乎不会导致测试二进制文件运行......我错过了什么吗?
  • 不要打死马,但我认为这值得再次提及。 Fraser 上面的评论提出了一个非常重要的观点:“您可以通过使用 ExternalProject_Add 而不是 add_subdirectory 来避免在自己的文件中包含 GTest 源。”有关详细信息,请参阅弗雷泽的答案和 cmets:stackoverflow.com/a/9695234/1735836
  • 就我而言,我还需要将pthread 添加到链接库中,将倒数第二行更改为target_link_libraries(runUnitTests gtest gtest_main pthread)
  • @weberc2 你必须运行make test 来运行测试,或者从构建目录运行ctest。运行 ctest -V 以查看 google 测试输出以及 ctest 输出。
  • 不起作用:i.imgur.com/ADGD0cs.png 。我没有 gtest-1.6.0 目录。它甚至有什么作用?我从 github 下载了googletest,我有一个googletest 目录。在googletest 目录中,我还有另一个googletest 目录,我应该在那里设置哪个?另外,我在dependencies 目录中有外部googletest 目录,而不是在CMake 项目的根目录中。那么我应该在 ADD_SUBDIRECTORY 函数中添加什么来使其真正起作用?
【解决方案2】:

这是我刚刚测试的一个完整的工作示例。它直接从 Web 下载,可以是固定的 tarball,也可以是最新的 subversion 目录。

cmake_minimum_required (VERSION 3.1)

project (registerer)

##################################
# Download and install GoogleTest

include(ExternalProject)
ExternalProject_Add(gtest
  URL https://github.com/google/googletest/archive/release-1.8.0.zip
  # Comment above line, and uncomment line below to use subversion.
  # SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/ 
  # Uncomment line below to freeze a revision (here the one for 1.7.0)
  # SVN_REVISION -r700
  
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest
  INSTALL_COMMAND ""
)
ExternalProject_Get_Property(gtest source_dir binary_dir)

################
# Define a test
add_executable(registerer_test registerer_test.cc)

######################################
# Configure the test to use GoogleTest
#
# If used often, could be made a macro.

add_dependencies(registerer_test gtest)
include_directories(${source_dir}/include)
target_link_libraries(registerer_test ${binary_dir}/libgtest.a)
target_link_libraries(registerer_test ${binary_dir}/libgtest_main.a)

##################################
# Just make the test runnable with
#   $ make test
    
enable_testing()
add_test(NAME    registerer_test 
         COMMAND registerer_test)

【讨论】:

  • 我不知道你为什么因此而被否决。您的解决方案可以防止有人将 Google Test 签入到版本控制中。感谢您的解决方案。
  • 您使用的 URL 现在已损坏。最新的 URL 是 https://github.com/google/googletest/archive/release-1.8.0.zip
  • 很好的答案。应该是 1 号。
  • 很好的答案!我们也可以使用GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.1 代替 URL
  • gtest 最新版本的 URL 是:https://github.com/google/googletest/archive/release-1.10.0.zip
【解决方案3】:

您可以两全其美。可以使用 ExternalProject 下载 gtest 源代码,然后使用 add_subdirectory() 将其添加到您的构建中。这样做有以下优点:

  • gtest 是作为主构建的一部分构建的,因此它使用相同的编译器标志等,从而避免出现问题中描述的问题。
  • 无需将 gtest 源代码添加到您自己的源代码树中。

以正常方式使用,ExternalProject 不会在配置时(即运行 CMake 时)进行下载和解包,但您只需做一些工作即可完成。我写了一篇关于如何做到这一点的博客文章,其中还包括一个通用实现,它适用于任何使用 CMake 作为其构建系统的外部项目,而不仅仅是 gtest。你可以在这里找到它们:

更新:这种方法现在也是part of the googletest documentation

【讨论】:

  • IMO,这可能是使用 CMake 项目实施 Google 测试的最简洁方式。希望版主多关注答案的内容和质量。
  • 链接的DownloadProject.cmake通用模块很棒。感觉就像 cmake 拥有一个包管理系统的基础,我所需要的只是一个指向 CMake 兼容 github url 的链接列表。
【解决方案4】:

最有可能的是,您的测试二进制文件和 Google 测试库之间编译器选项的差异应归咎于此类错误。这就是为什么建议以源代码形式引入 Google Test 并与您的测试一起构建它。在 CMake 中很容易做到。您只需使用 gtest 根目录的路径调用 ADD_SUBDIRECTORY,然后您就可以使用那里定义的公共库目标(gtestgtest_main)。 googletestframework 组中的CMake thread 中有更多背景信息。

[编辑] BUILD_SHARED_LIBS 选项目前仅在 Windows 上有效。它指定您希望 CMake 构建的库的类型。如果将其设置为ON,CMake 会将它们构建为 DLL,而不是静态库。在这种情况下,您必须使用 -DGTEST_LINKED_AS_SHARED_LIBRARY=1 构建测试,并将 CMake 生成的 DLL 文件复制到包含测试二进制文件的目录中(默认情况下,CMake 将它们放在单独的输出目录中)。除非静态库中的 gtest 对您不起作用,否则不设置该选项会更容易。

【讨论】:

  • 非常感谢,没有意识到您可以在这样的同一个 CMakeLists 中构建完全独立的项目。我现在可以肯定地说 EXPECT_EQ(1.0 == 1.0) 通过并且 EXPECT_EQ(0.0 == 1.0) 失败。现在是进行更多实际测试的时候了……
【解决方案5】:

OP 使用的是 Windows,而今天使用 GTest 更简单的方法是使用 vcpkg+cmake。


按照https://github.com/microsoft/vcpkg 安装vcpkg,并确保您可以从cmd 行运行vcpkg。记下 vcpkg 安装文件夹,例如。 C:\bin\programs\vcpkg.

使用vcpkg install gtest 安装 gtest:这将下载、编译和安装 GTest。

如下使用 CmakeLists.txt:注意我们可以使用 targets 而不是包含文件夹。

cmake_minimum_required(VERSION 3.15)
project(sample CXX)
enable_testing()
find_package(GTest REQUIRED)
add_executable(test1 test.cpp source.cpp)
target_link_libraries(test1 GTest::GTest GTest::Main)
add_test(test-1 test1)

使用以下命令运行 cmake:(必要时编辑 vcpkg 文件夹,并确保 vcpkg.cmake 工具链文件的路径正确)

cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:\bin\programs\vcpkg\scripts\buildsystems\vcpkg.cmake

并像往常一样使用cmake --build build 构建。 请注意,vcpkg 还会将所需的 gtest(d).dll/gtest(d)_main.dll 从安装文件夹复制到 Debug/Release 文件夹。

使用cd build &amp; ctest 进行测试。

【讨论】:

    【解决方案6】:

    在进行了更多挖掘之后,我认为我的问题与我正在构建 gtest 的库类型有关。使用 CMake 构建 gtest 时,如果未选中 BUILD_SHARED_LIBS,并且我将我的程序与这些 .lib 文件链接起来,则会出现上述错误。但是,如果检查了 BUILD_SHARED_LIBS,那么我会生成一组 .lib 和 .dll 文件。现在链接这些 .lib 文件时,程序会编译,但运行时会抱怨找不到 gtest.dll。

    这是因为如果您想将 gtest 用作共享库,则必须将 -DGTEST_LINKED_AS_SHARED_LIBRARY=1 添加到项目的编译器定义中。

    您也可以使用静态库,前提是您使用 gtest_force_shared_crt 选项编译它以消除您看到的错误。

    我喜欢这个库,但将它添加到项目中确实很痛苦。除非你挖掘(和破解)gtest cmake 文件,否则你没有机会做对。耻辱。特别是我不喜欢将 gtest 添加为源的想法。 :)

    【讨论】:

      【解决方案7】:

      正如@Patricia 在接受的答案中的评论和@Fraser 对原始问题的评论的更新一样,如果您有权访问 CMake 3.11+,则可以使用 CMake 的 FetchContent 函数。

      CMake的FetchContent页面以googletest为例!

      我对已接受的答案进行了小修改:

      cmake_minimum_required(VERSION 3.11)
      project(basic_test)
      
      set(GTEST_VERSION 1.6.0 CACHE STRING "Google test version")
      
      ################################
      # GTest
      ################################
      FetchContent_Declare(googletest
      GIT_REPOSITORY https://github.com/google/googletest.git
      GIT_TAG release-${GTEST_VERSION})
      
      FetchContent_GetProperties(googletest)
      if(NOT googletest_POPULATED)
        FetchContent_Populate(googletest)
        add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
      endif()
      
      enable_testing()
      
      ################################
      # Unit Tests
      ################################
      # Add test cpp file
      add_executable(runUnitTests testgtest.cpp)
      
      # Include directories
      target_include_directories(runUnitTests 
                            $<TARGET_PROPERTY:gtest,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>
                            $<TARGET_PROPERTY:gtest_main,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>)
      
      # Link test executable against gtest & gtest_main
      target_link_libraries(runUnitTests gtest
                                         gtest_main)
      
      add_test(runUnitTests runUnitTests)
      

      您可以使用 gtest 和 gtest_main 目标的 INTERFACE_SYSTEM_INCLUDE_DIRECTORIES 目标属性,因为它们是在 google test CMakeLists.txt 脚本中设置的。

      【讨论】:

      • 在 CMake >= v3.14 中,您可以放弃显式的 target_include_directories 并改用 FetchContent_MakeAvailable(googletest)。这将填充内容并将其添加到主构建中。 CMake FetchContent - more info
      【解决方案8】:

      您和 VladLosevs 的解决方案可能比我的更好。但是,如果您想要一个蛮力解决方案,请尝试以下操作:

      SET(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:\"msvcprtd.lib;MSVCRTD.lib\")
      
      FOREACH(flag_var
          CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
          CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
          if(${flag_var} MATCHES "/MD")
              string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
          endif(${flag_var} MATCHES "/MD")
      ENDFOREACH(flag_var)
      

      【讨论】:

        【解决方案9】:

        我从这个线程的答案中提炼出来的最简单的 CMakeLists.txt 和一些试验和错误是:

        project(test CXX C)
        cmake_minimum_required(VERSION 2.6.2)
        
        #include folder contains current project's header filed
        include_directories("include")
        
        #test folder contains test files
        set (PROJECT_SOURCE_DIR test) 
        add_executable(hex2base64 ${PROJECT_SOURCE_DIR}/hex2base64.cpp)
        
        # Link test executable against gtest nothing else required
        target_link_libraries(hex2base64 gtest pthread)
        

        Gtest 应该已经安装在您的系统上。

        【讨论】:

        • 在 CMake 中添加这样的库确实不是一个好习惯。 cmake 的主要目标之一是永远不必做出像“这个库应该已经安装......”这样的假设。 CMake 检查库是否在这里,如果没有,则抛出错误。
        【解决方案10】:

        我决定将一些通用的东西放在一起,快速展示一种与之前发布的答案不同的方法,希望它可以帮助某人。以下内容在我的 Mac 上为我工作。首先,我为 gtests 运行了设置命令。我只是使用我找到的脚本来设置所有内容。

        #!/usr/bin/env bash
        
        # install gtests script on mac
        # https://gist.github.com/butuzov/e7df782c31171f9563057871d0ae444a
        
        #usage
        # chmod +x ./gtest_installer.sh
        # sudo ./gtest_installer.sh
        
        # Current directory
        __THIS_DIR=$(pwd)
        
        
        # Downloads the 1.8.0 to disc
        function dl {
            printf "\n  Downloading Google Test Archive\n\n"
            curl -LO https://github.com/google/googletest/archive/release-1.8.0.tar.gz
            tar xf release-1.8.0.tar.gz
        }
        
        # Unpack and Build
        function build {
            printf "\n  Building GTest and Gmock\n\n"
            cd googletest-release-1.8.0
            mkdir build 
            cd $_
            cmake -Dgtest_build_samples=OFF -Dgtest_build_tests=OFF ../
            make
        }
        
        # Install header files and library
        function install {
            printf "\n  Installing GTest and Gmock\n\n"
        
            USR_LOCAL_INC="/usr/local/include"
            GTEST_DIR="/usr/local/Cellar/gtest/"
            GMOCK_DIR="/usr/local/Cellar/gmock/"
        
            mkdir $GTEST_DIR
        
            cp googlemock/gtest/*.a $GTEST_DIR
            cp -r ../googletest/include/gtest/  $GTEST_DIR
            ln -snf $GTEST_DIR $USR_LOCAL_INC/gtest
            ln -snf $USR_LOCAL_INC/gtest/libgtest.a /usr/local/lib/libgtest.a
            ln -snf $USR_LOCAL_INC/gtest/libgtest_main.a /usr/local/lib/libgtest_main.a
        
            mkdir $GMOCK_DIR
            cp googlemock/*.a   $GMOCK_DIR
            cp -r ../googlemock/include/gmock/  $GMOCK_DIR
            ln -snf $GMOCK_DIR $USR_LOCAL_INC/gmock
            ln -snf $USR_LOCAL_INC/gmock/libgmock.a /usr/local/lib/libgmock.a
            ln -snf $USR_LOCAL_INC/gmock/libgmock_main.a /usr/local/lib/libgmock_main.a
        }
        
        # Final Clean up.
        function cleanup {
            printf "\n  Running Cleanup\n\n"
        
            cd $__THIS_DIR
            rm -rf $(pwd)/googletest-release-1.8.0
            unlink $(pwd)/release-1.8.0.tar.gz
        }
        
        dl && build && install && cleanup 
        

        接下来,我做了一个简单的文件夹结构,并写了一些快速类

        utils/
          cStringUtils.cpp
          cStringUtils.h
          CMakeLists.txt
        utils/tests/
            gtestsMain.cpp
            cStringUtilsTest.cpp
            CMakeLists.txt
        

        我为 utils 文件夹创建了一个顶级 CMakeLists.txt,为测试文件夹创建了一个 CMakeLists.txt

        cmake_minimum_required(VERSION 2.6)
        
        project(${GTEST_PROJECT} C CXX)
        
        set(CMAKE_C_STANDARD 98)
        set(CMAKE_CXX_STANDARD 98)
        
        #include .h and .cpp files in util folder
        include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
        
        ##########
        # GTests
        #########
        add_subdirectory(tests)
        

        这是测试文件夹中的 CMakeLists.txt

        cmake_minimum_required(VERSION 2.6)
        
        set(GTEST_PROJECT gtestProject)
        
        enable_testing()
        
        message("Gtest Cmake")
        
        find_package(GTest REQUIRED)
        
        # The utils, test, and gtests directories
        include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
        include_directories("/usr/local/Cellar/gtest/include")
        include_directories("/usr/local/Cellar/gtest/lib")
        
        set(SOURCES
          gtestsMain.cpp
          ../cStringUtils.cpp
          cStringUtilsTest.cpp
        )
        
        set(HEADERS
          ../cStringUtils.h
        )
        
        add_executable(${GTEST_PROJECT} ${SOURCES})
        target_link_libraries(${GTEST_PROJECT} PUBLIC
          gtest
          gtest_main
        )
        
        add_test(${GTEST_PROJECT} ${GTEST_PROJECT})
        

        那么剩下的就是编写一个示例 gtest 和 gtest main

        示例 gtest

        #include "gtest/gtest.h"
        #include "cStringUtils.h"
        
        namespace utils
        {
        
        class cStringUtilsTest : public ::testing::Test {
        
         public:
        
          cStringUtilsTest() : m_function_param(10) {}
          ~cStringUtilsTest(){}
        
         protected:
          virtual void SetUp() 
          {
            // declare pointer 
            pFooObject = new StringUtilsC();    
          }
        
          virtual void TearDown() 
          {
            // Code here will be called immediately after each test
            // (right before the destructor).
            if (pFooObject != NULL)
            {
              delete pFooObject;
              pFooObject = NULL;
            }
          }
        
        
          StringUtilsC fooObject;              // declare object
          StringUtilsC *pFooObject;
          int m_function_param;                // this value is used to test constructor
        };
        
        TEST_F(cStringUtilsTest, testConstructors){
            EXPECT_TRUE(1);
        
          StringUtilsC fooObject2 = fooObject; // use copy constructor
        
        
          fooObject.fooFunction(m_function_param);
          pFooObject->fooFunction(m_function_param);
          fooObject2.fooFunction(m_function_param);
        }
        
        } // utils end
        

        示例 gtest main

        #include "gtest/gtest.h"
        #include "cStringUtils.h"
        
        int main(int argc, char **argv) {
          ::testing::InitGoogleTest(&argc, argv); 
          return RUN_ALL_TESTS();
        }
        

        然后我可以使用 utils 文件夹中的以下命令编译和运行 gtests

        cmake .
        make 
        ./tests/gtestProject
        

        【讨论】:

          猜你喜欢
          • 2016-11-04
          • 1970-01-01
          • 1970-01-01
          • 2012-11-11
          • 2015-03-15
          • 2017-11-27
          • 2021-06-11
          • 1970-01-01
          • 2019-12-09
          相关资源
          最近更新 更多