GTest 및 CMake 작업을 시작하는 방법


125

나는 최근에 C ++ 프로젝트를 컴파일하기 위해 CMake를 사용하여 판매되었으며 이제 내 코드에 대한 일부 단위 테스트 작성을 시작하고 싶습니다. 이 문제를 해결하기 위해 Google 테스트 유틸리티를 사용하기로 결정했지만 시작하는 데 도움이 필요합니다.

저는 하루 종일 다양한 가이드와 예제를 읽고 있었는데 여기에는 Primer , IBM 소개 , SO에 대한 몇 가지 질문 ( 여기여기 ) 및 제가 놓친 다른 소스가 포함됩니다. 나는 거기에 많은 것이 있다는 것을 알고 있지만 어떻게 든 여전히 어려움을 겪고 있습니다.

현재 gtest를 컴파일 / 설치했고 작동하지 않는지 확인하기 위해 가장 기본적인 테스트를 구현하려고합니다. 유일한 소스 파일 (testgtest.cpp)은 이전의 대답 :

#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
)

cpp 파일의 끝에 main을 제공하는 대신 gtest_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을 찾을 수 없다고 불평합니다.

a SHARED와 not SHARED라이브러리 의 차이점은 무엇이며 공유하지 않도록 선택하면 왜 작동하지 않습니까? 내 프로젝트에 대한 CMakeLists.txt에 누락 된 옵션이 있습니까?


4
ExternalProject_Add대신 을 사용하여 GTest 소스를 직접 포함하지 않을 수 있습니다 add_subdirectory. 자세한 내용은 이 답변 을 참조하십시오.
Fraser

위의 솔루션 예에서 $ {gtest_SOURCE_DIR}에 액세스 할 수있는 이유는 무엇입니까? 그 변수는 어떻게 / 어디에서 선언됩니까?
dmonopoly

오, gtest-1.6.0 / CMakeLists.txt : "project (gtest CXX C)"에 선언되어 gtest_SOURCE_DIR 및 gtest_BINARY_DIR 변수를 사용할 수 있습니다.
dmonopoly

1
무엇을 enable_testing()합니까?
updogliu

1
@updogliu : ctest 및 'test'(또는 'RUN_TESTS') 대상을 활성화합니다. add_test () cmake 명령과 함께 재생됩니다.
Ela782

답변:


76

해결책은 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 )

3
add_test ()가 무엇을하는지 잘 모르겠지만 테스트 바이너리가 실행되지 않는 것 같습니다 ... 뭔가 빠졌나요?
weberc2

4
죽은 말을 이길 수는 없지만 다시 언급 할 가치가 있다고 생각했습니다. 위의 Fraser의 설명은 매우 중요한 점을 보여줍니다. "add_subdirectory 대신 ExternalProject_Add를 사용하여 GTest 소스를 직접 포함하지 않을 수 있습니다." 자세한 내용은 Fraser의 답변과 의견을 참조하십시오. stackoverflow.com/a/9695234/1735836
Patricia

1
내 경우, 나는 또한 추가 할 필요 pthread에 두 번째 마지막 줄을 변경, 링크 라이브러리target_link_libraries(runUnitTests gtest gtest_main pthread)
panmari

3
@ weberc2 make test테스트 를 실행 하려면 실행하거나 ctest빌드 디렉터리에서 실행 해야합니다 . ctest -VGoogle 테스트 출력과 출력을 보려면 실행 하십시오 ctest.
Patrick

38

방금 테스트 한 완전한 작업 예제가 있습니다. 웹, 고정 tarball 또는 최신 하위 버전 디렉토리에서 직접 다운로드합니다.

cmake_minimum_required (VERSION 3.1)

project (registerer)

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

include(ExternalProject)
ExternalProject_Add(gtest
  URL https://googletest.googlecode.com/files/gtest-1.7.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)

7
왜 당신이 이것에 대해 반대표를 받았는지 모르겠습니다. 귀하의 솔루션은 누군가가 Google 테스트에서 버전 관리를 확인하지 못하도록합니다. 솔루션에 대한 명성.
Sal

4
이제 사용하는 URL이 손상되었습니다. 최신 URL은https://github.com/google/googletest/archive/release-1.8.0.zip
oscfri

좋은 대답입니다. 1 번이어야합니다.
Mr00Anderson

1
훌륭한 대답! 또한 우리가 사용할 수있는 GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.1대신 URL의
TingQian LI

gtest 최신 릴리스의 URL은 다음과 같습니다.https://github.com/google/googletest/archive/release-1.10.0.zip
vahancho

16

두 세계의 장점을 모두 얻을 수 있습니다. 사용할 수 있습니다 ExternalProjectgtest 소스를 다운로드하고 사용하는 add_subdirectory()빌드에 추가 할 수 있습니다. 이것은 다음과 같은 장점이 있습니다.

  • gtest는 메인 빌드의 일부로 빌드되므로 동일한 컴파일러 플래그 등을 사용하므로 질문에 설명 된 것과 같은 문제를 방지합니다.
  • 자체 소스 트리에 gtest 소스를 추가 할 필요가 없습니다.

일반적인 방식으로 사용되는 ExternalProject는 구성시 (예 : CMake가 실행될 때) 다운로드 및 압축 해제를 수행하지 않지만 약간의 작업만으로 수행 할 수 있습니다. gtest뿐만 아니라 CMake를 빌드 시스템으로 사용하는 모든 외부 프로젝트에서 작동하는 일반화 된 구현도 포함하는이 작업을 수행하는 방법에 대한 블로그 게시물을 작성했습니다. 여기에서 찾을 수 있습니다.

업데이트 : 이 접근 방식은 이제 googletest 문서의 일부입니다 .


2
IMO, 이것은 아마도 CMake 프로젝트로 Google 테스트를 구현하는 가장 깨끗한 방법 일 것입니다. 중재자가 답변의 내용과 질에 더 많은 관심을 기울 였으면합니다.
NameRakes

링크 된 DownloadProject.cmake 일반화 모듈은 훌륭합니다. CMake 호환 github URL에 대한 링크 목록 만 있으면되는 패키지 관리 시스템을 갖춘 cmake의 기초처럼 느껴집니다.
Josh Peak

13

대부분의 경우 테스트 바이너리와 Google 테스트 라이브러리 간의 컴파일러 옵션 차이는 이러한 오류에 대한 책임입니다. 그렇기 때문에 Google 테스트를 소스 형식으로 가져와 테스트와 함께 빌드하는 것이 좋습니다. CMake에서하는 것은 매우 쉽습니다. ADD_SUBDIRECTORYgtest 루트에 대한 경로로 호출 하기 만하면 거기에 정의 된 공용 라이브러리 대상 ( gtestgtest_main)을 사용할 수 있습니다 . 이 CMake 스레드 에 더 많은 배경 정보가 있습니다.googletestframework 그룹 에 있습니다.

BUILD_SHARED_LIBS옵션은 현재 Windows에서만 유효합니다. CMake에서 빌드 할 라이브러리 유형을 지정합니다. 로 설정하면 ONCMake가 정적 라이브러리가 아닌 DLL로 빌드합니다. 이 경우 -DGTEST_LINKED_AS_SHARED_LIBRARY = 1로 테스트를 빌드하고 CMake에서 생성 한 DLL 파일을 테스트 바이너리가있는 디렉터리에 복사해야합니다 (CMake는 기본적으로 별도의 출력 디렉터리에 배치). 정적 lib의 gtest가 작동하지 않는 한 해당 옵션을 설정하지 않는 것이 더 쉽습니다.


1
감사합니다. 이와 같은 동일한 CMakeLists 내에서 완전히 별개의 프로젝트를 빌드 할 수 있다는 사실을 몰랐습니다. 이제 EXPECT_EQ (1.0 == 1.0) 통과하고 EXPECT_EQ (0.0 == 1.0) 실패한다고 안전하게 말할 수 있습니다. 이제 더 실제 시험 시간 ...
크리스

2

좀 더 파고 들었을 때 내 문제는 내가 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를 소스로 추가하는 아이디어가 마음에 들지 않습니다. :)


1

OP는 Windows를 사용하고 있으며 오늘날 GTest를 사용하는 훨씬 쉬운 방법은 vcpkg + cmake를 사용하는 것입니다.


https://github.com/microsoft/vcpkg 에 따라 vcpkg를 설치 vcpkg하고 cmd 줄에서 실행할 수 있는지 확인합니다 . vcpkg 설치 폴더를 기록해 둡니다. C:\bin\programs\vcpkg.

다음을 사용하여 gtest 설치 vcpkg install gtest: GTest를 다운로드, 컴파일 및 설치합니다.

아래와 같이 CmakeLists.txt를 사용하십시오 . 폴더를 포함하는 대신 대상 을 사용할 수 있습니다 .

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 & ctest.


0

귀하와 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)

0

이 스레드의 답변과 시행 착오에서 추출한 가장 간단한 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는 라이브러리가 여기에 있는지 확인하고 그렇지 않은 경우 오류가 발생합니다.
Adrien BARRAL 2016

0

수락 된 답변의 @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)

INTERFACE_SYSTEM_INCLUDE_DIRECTORIESgoogle test CMakeLists.txt 스크립트에 설정된대로 gtest 및 gtest_main 대상 의 target 속성을 사용할 수 있습니다 .


CMake> = v3.14에서는 명시적인 것을 무시 target_include_directories하고 FetchContent_MakeAvailable(googletest)대신 사용할 수 있습니다 . 이렇게하면 콘텐츠가 채워지고 기본 빌드에 추가됩니다. CMake FetchContent-추가 정보
67hz

0

나는 누군가에게 도움이 될 수 있기를 희망하면서 이전에 게시 한 답변과 다른 방법을 보여주는 일반적인 것을 함께 던지기로 결정했습니다. 다음은 내 Mac에서 나를 위해 일했습니다. 먼저 gtest에 대한 설정 명령을 실행했습니다. 방금 찾은 스크립트를 사용하여 모든 것을 설정했습니다.

#!/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 메인

#include "gtest/gtest.h"
#include "cStringUtils.h"

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv); 
  return RUN_ALL_TESTS();
}

그런 다음 utils 폴더에서 다음 명령을 사용하여 gtest를 컴파일하고 실행할 수 있습니다.

cmake .
make 
./tests/gtestProject
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.