Як почати працювати з GTest та CMake


125

Нещодавно мене продали на використанні CMake для складання моїх проектів на C ++, і зараз я хотів би почати писати деякі тести для свого коду. Я вирішив скористатись утилітою 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
)

Зауважте, що я вирішив зв’язатись з gtest_main замість надання основного в кінці файлу cpp, оскільки я вважаю, що це дозволить мені легше масштабувати тестування на декілька файлів.

Під час створення сформованого файлу .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)

що, на мою думку, означає, що я не вдається успішно зв’язатися з найшвидшими бібліотеками. Я переконався, що при підключенні до бібліотек налагодження я намагався будувати в режимі налагодження.

EDIT

Зробивши ще кілька копань, я думаю, що моя проблема пов'язана з типом бібліотеки, в яку я будую. Під час створення gtest з CMake, якщо BUILD_SHARED_LIBSце не встановлено, я пов’язую свою програму з цими .lib-файлами, я отримую вказані вище помилки. Однак якщо BUILD_SHARED_LIBSце встановлено, я створюю набір файлів .lib та .dll. Під час посилання на ці файли .lib програма компілює, але при запуску скаржиться, що не може знайти gtest.dll.

У чому полягають відмінності між бібліотекою " a" SHAREDта " SHAREDnon", і якщо я вибираю не спільний, чому це не працює? Чи є в моєму проекті CMakeLists.txt варіант, який мені не вистачає?


4
Ви можете уникнути включення джерел GTest у свої власні, використовуючи, ExternalProject_Addа не add_subdirectory. Детальніше див. У цій відповіді .
Фрейзер

Чому ми маємо доступ до $ {gtest_SOURCE_DIR} у прикладі рішення вище? Як / де оголошується ця змінна?
dmonopoly

О, це оголошено в gtest-1.6.0 / CMakeLists.txt: "проект (gtest CXX C)", який робить доступними змінні gtest_SOURCE_DIR та gtest_BINARY_DIR.
dmonopoly

1
Що робить enable_testing()?
updogliu

1
@updogliu: Він дає можливість тестувати ціль та "тест" (або "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
Не бити мертвого коня, але я подумав, що це варто згадати ще раз. Коментар Фрейзера вище робить дуже важливим моментом: "Ви можете уникнути включення джерел GTest у свої власні, використовуючи ExternalProject_Add, а не add_subdirectory." Відповідь та коментарі Фрейзера див. Тут: stackoverflow.com/a/9695234/1735836
Патрісія

1
У моєму випадку мені також потрібно було додати pthreadдо пов’язаних бібліотек, змінивши другий останній рядок наtarget_link_libraries(runUnitTests gtest gtest_main pthread)
panmari

3
@ weberc2 Вам потрібно запустити, make testщоб запустити тести або запустити ctestз каталогу збирання. Запустіть, ctest -Vщоб побачити тестовий вихід google, а також ctestвихід.
Патрік

38

Ось повний робочий приклад, який я щойно перевірив. Він завантажується безпосередньо з Інтернету, або з фіксованим тарболом, або з останнього каталогу підривних програм.

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 на контроль версій. Кудо для вашого рішення.
Сал

4
URL-адреса, яку ви використовуєте, тепер зламана. https://github.com/google/googletest/archive/release-1.8.0.zip
Актуальною

Чудова відповідь. Має бути номер 1.
Містер00Андерсон

1
точна відповідь! також ми можемо використовувати GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.1замість URL
TingQian LI

URL-адреса до найсвіжішого випуску:https://github.com/google/googletest/archive/release-1.10.0.zip
vahancho

16

Ви можете отримати найкраще з обох світів. Можна використати ExternalProjectдля завантаження джерела gtest, а потім використати, add_subdirectory()щоб додати його до своєї збірки. Це має такі переваги:

  • gtest будується як частина вашої основної збірки, тому він використовує ті ж прапори компілятора тощо, а значить, уникає проблем, як описані в питанні.
  • Немає необхідності додавати джерела gtest до власного дерева джерел.

Застосовуваний у звичайному режимі, ExternalProject не буде виконувати завантаження та розпакування під час налаштування (тобто, коли CMake запускається), але ви можете змусити це зробити за допомогою лише трохи роботи. Я написав публікацію в блозі про те, як це зробити, що також включає узагальнену реалізацію, яка працює для будь-якого зовнішнього проекту, який використовує CMake як свою систему збирання, а не просто gtest. Ви можете знайти їх тут:

Оновлення: Цей підхід тепер також є частиною документації Google .


2
IMO, це, мабуть, найчистіший спосіб впровадити тест Google за допомогою проекту CMake. Хочеться, щоб модератори приділяли більше уваги змісту та якості відповідей.
NameRakes

Зв'язаний модуль узагальнення DownloadProject.cmake чудовий. Схоже, що основою для cmake є система управління пакетами, де мені потрібно лише список посилань на сумісні URL-адреси github, сумісні з CMake.
Джош Пік

13

Швидше за все, в таких помилках винна різниця в параметрах компілятора між тестовим бінарним файлом та бібліотекою Google Test. Ось чому рекомендується принести тест Google у вихідній формі та створити його разом із тестами. Це дуже просто зробити в CMake. Ви просто Invoke ADD_SUBDIRECTORYзі шляху до GTEST кореня , а потім ви можете використовувати державні цільові бібліотеки ( gtestі gtest_main) визначається там. Більше довідкової інформації в цій темі CMake в групі googletestframework.

[редагувати] BUILD_SHARED_LIBSОпція наразі діє лише в Windows. Він визначає тип бібліотек, які потрібно створити CMake. Якщо встановити його ON, CMake створить їх у вигляді DLL на відміну від статичних ліб. У такому випадку вам доведеться скласти свої тести за допомогою -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 і скопіювати файли DLL, вироблені CMake, у каталог із тестовим бінарним файлом (CMake розміщує їх у окремому каталозі виводу за замовчуванням). Якщо gest у статичній вкладці не працює для вас, простіше не встановити цю опцію.


1
Велике спасибі, не зрозумів, що ти можеш будувати цілком окремі проекти в межах таких же CMakeList. Зараз я сміливо можу сказати, що EXPECT_EQ (1.0 == 1.0) проходить, а EXPECT_EQ (0.0 == 1.0) провалюється. Зараз час для більш реальних тестів ...
Кріс

2

Зробивши ще кілька копань, я думаю, що моя проблема пов'язана з типом бібліотеки, в яку я будую. Під час створення gtest з CMake, якщо BUILD_SHARED_LIBS не встановлений, і я пов'язую свою програму з цими .lib-файлами, я отримую помилки, згадані вище. Однак якщо BUILD_SHARED_LIBS встановлений, я створюю набір файлів .lib та .dll. Під час посилання на ці файли .lib програма компілює, але при запуску скаржиться, що не може знайти gtest.dll.

Це тому, що вам потрібно додати -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 до визначень компілятора у вашому проекті, якщо ви хочете використовувати gtest як спільну бібліотеку.

Ви також можете використовувати статичні бібліотеки, якщо ви компілювали їх з опцією gtest_force_shared_crt для усунення помилок, які ви бачили.

Мені подобається бібліотека, але додавання її до проекту - це справжній біль. І у вас немає шансів зробити це правильно, якщо ви не викопаєте (і не зламаєте) файли gtest cmake. Сором. Зокрема, мені не подобається ідея додавання gtest як джерела. :)


1

ОП використовує Windows, і набагато простішим способом використання GTest сьогодні є vcpkg + cmake.


Встановіть vcpkg відповідно до https://github.com/microsoft/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 з папки установки у папки налагодження / випуску.

Тест с cd build & ctest.


0

Ваші рішення та рішення ВладЛосева, напевно, краще, ніж моє. Однак ви хочете скористатись жорстоким рішенням:

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 перевірте, чи знаходиться тут бібліотека, і якщо ні, помилка видається.
Адрієн БАРРАЛ

0

Так само як оновлення до коментаря @ Patricia у прийнятій відповіді та коментаря @ Fraser до оригінального питання, якщо у вас є доступ до CMake 3.11+, ви можете скористатися функцією FetchContent CMake .

Сторінка FetchContent CMake використовує 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_DIRECTORIESцільове властивість цілей gtest та gtest_main, як вони встановлені в сценарії тестування google CMakeLists.txt .


У CMake> = v3.14 ви можете відмовитися від явного target_include_directoriesта використовувати FetchContent_MakeAvailable(googletest)замість цього. Це одночасно заповнить вміст і додасть його до основної збірки. CMake FetchContent - додаткова інформація
67hz

0

Я вирішив зібрати щось загальне разом, по-справжньому швидко продемонструвавши інший спосіб зробити це, ніж відповіді, раніше розміщені, сподіваючись, що це може комусь допомогти. Наступне працювало для мене на моєму комп'ютері. По-перше, я запускав команди настройки для 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

Я створив CMakeLists.txt верхнього рівня для папки utils і 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();
}

Потім я можу компілювати та запускати gtests за допомогою наступних команд із папки utils

cmake .
make 
./tests/gtestProject
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.