GTestとCMakeの使用を開始する方法


125

私は最近、C ++プロジェクトをコンパイルするためにCMakeを使用することで売却されました。今、私のコードのいくつかの単体テストを書き始めたいと思います。私はこれを支援するためにGoogle Testユーティリティを使用することを決定しましたが、開始するにはいくつかの助けが必要です。

一日中、さまざまなガイドや例を読んでいます。入門書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ファイルの最後にメインを提供するのではなく、gtest_mainに対してリンクすることを選択したことに注意してください。これにより、テストを複数のファイルに簡単に拡張できるようになると信じています。

(Visual C ++ 2010 Expressで)生成された.slnファイルをビルドすると、残念ながらフォームのエラーの長いリストが表示されます

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が見つからないというメッセージが表示されます。

SHAREDa SHAREDライブラリとnot ライブラリの違いは何ですか?共有しないことを選択した場合、なぜ機能しないのですか?CMakeLists.txtにプロジェクトのオプションがありませんか?


4
ExternalProject_Addではなくを使用することで、独自のGTestソースを含めることを回避できますadd_subdirectory。詳細については、この回答を参照してください。
Fraser

上記のソリューション例で$ {gtest_SOURCE_DIR}にアクセスできるのはなぜですか?その変数はどのように/どこで宣言されていますか?
dmonopoly 2013年

ああ、gtest-1.6.0 / CMakeLists.txt: "project(gtest CXX C)"で宣言されています。これにより、変数gtest_SOURCE_DIRおよびgtest_BINARY_DIRが使用可能になります。
dmonopoly 2013年

1
何をしenable_testing()ますか?
updogliu 2013年

1
@updogliu:ctestと 'test'(または 'RUN_TESTS')ターゲットを有効にします。add_test()cmakeコマンドと一緒に再生されます。
Ela782 14

回答:


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ソースを独自に含めることを回避できます。」詳細については、フレーザーの回答とコメントをここで参照してください: stackoverflow.com/a/9695234/1735836
Patricia

1
私の場合、pthreadリンクされたライブラリに追加する必要もあります。最後の2行目を次のように変更しますtarget_link_libraries(runUnitTests gtest gtest_main pthread)
panmari

3
@ weberc2 make testテストを実行するには、またはctestビルドディレクトリから実行する必要があります。実行ctest -Vして、Googleテストの出力と出力を確認しctestます。
Patrick

38

これが私がテストした完全な実用例です。これは、固定tarballまたは最新のsubversionディレクトリのいずれかで、Webから直接ダウンロードします。

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 Testをバージョン管理にチェックインする必要を防ぎます。ソリューションに対する称賛。
2015年

4
使用しているURLは壊れています。最新のURLはhttps://github.com/google/googletest/archive/release-1.8.0.zip
oscfri 2018年

すばらしい答えです。番号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

あなたは両方の世界のベストを得ることができます。を使用ExternalProjectしてgtestソースをダウンロードし、それを使用add_subdirectory()してビルドに追加することができます。これには次の利点があります。

  • gtestはメインビルドの一部としてビルドされるため、同じコンパイラフラグなどを使用するため、質問で説明したような問題を回避できます。
  • 独自のソースツリーにgtestソースを追加する必要はありません。

通常の方法で使用すると、ExternalProjectは構成時(つまり、CMakeの実行時)にダウンロードとアンパックを行いませんが、ほんの少しの作業でそれを行うことができます。これを行う方法についてブログ投稿を書いています。これには、gtestだけでなく、CMakeをビルドシステムとして使用する外部プロジェクトで機能する一般的な実装も含まれています。あなたはここでそれらを見つけることができます:

更新:このアプローチは、現在googletestのドキュメントの一部でもあります


2
IMO、これはおそらく、CMakeプロジェクトでGoogleテストを実装する最もクリーンな方法です。モデレーターが回答の内容と質にもっと注意を払ってくれることを望みます。
NameRakes 2017年

リンクされたDownloadProject.cmake汎用モジュールは素晴らしいです。cmakeが、必要なのはCMake互換のgithub URLへのリンクのリストだけであるパッケージ管理システムの基礎のように思えます。
ジョシュピーク

13

ほとんどの場合、テストバイナリとGoogleテストライブラリのコンパイラオプションの違いは、そのようなエラーのせいです。そのため、ソース形式でGoogle Testを取り込み、テストと一緒にビルドすることをお勧めします。CMakeで行うのは非常に簡単です。ADD_SUBDIRECTORYgtestルートへのパスを指定して呼び出すだけ で、そこに定義されているパブリックライブラリターゲット(gtestおよびgtest_main)を使用できます。googletestframeworkグループのこのCMakeスレッドには、より多くの背景情報があります。

[編集]このBUILD_SHARED_LIBSオプションは、現時点ではWindowsでのみ有効です。CMakeでビルドするライブラリのタイプを指定します。これをに設定するとON、CMakeは静的ライブラリではなくDLLとしてビルドします。その場合、-DGTEST_LINKED_AS_SHARED_LIBRARY = 1を使用してテストをビルドし、CMakeによって生成されたDLLファイルをテストバイナリのあるディレクトリにコピーする必要があります(CMakeは、デフォルトで別の出力ディレクトリにそれらを配置します)。static libのgtestが機能しない場合を除き、そのオプションを設定しない方が簡単です。


1
感謝します。このように同じCMakeLists内で完全に別のプロジェクトを構築できることに気付きませんでした。EXPECT_EQ(1.0 == 1.0)は成功し、EXPECT_EQ(0.0 == 1.0)は失敗したと私は安全に言うことができます。さて、実際のテストの時間です...
Chris

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の主な目的の1つは、「このライブラリはすでにインストールされているはずです...」のような仮定を決してする必要がないことです。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_directoriesFetchContent_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を作成し、testsフォルダー用に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)

これは、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フォルダーから次のコマンドを使用してgtestsをコンパイルおよび実行できます

cmake .
make 
./tests/gtestProject
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.