web-dev-qa-db-fra.com

Comment commencer à travailler avec GTest et CMake

J'ai récemment appris à utiliser CMake pour compiler mes projets C++ et j'aimerais maintenant commencer à écrire des tests unitaires pour mon code. J'ai décidé d'utiliser l'utilitaire Google Test pour résoudre ce problème, mais j'ai besoin d'aide pour démarrer.

Toute la journée, j'ai lu divers guides et exemples, dont Primer , un introduction à IBM et quelques questions sur SO ( ici et ici ) ainsi que d’autres sources que j’ai perdues de vue. Je me rends compte qu’il y en a beaucoup, mais j’ai toujours des difficultés.

J'essaie actuellement de mettre en œuvre le test le plus élémentaire, pour confirmer que j'ai bien compilé/installé gtest et que cela ne fonctionne pas. Le seul fichier source (testgtest.cpp) provient presque exactement de this réponse précédente:

#include <iostream>

#include "gtest/gtest.h"

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

et mon CMakeLists.txt associé est la suivante:

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
)

Notez que j’ai choisi d’établir un lien avec gtest_main au lieu de fournir le fichier principal à la fin du fichier cpp, car j’estime que cela me permettra d’effectuer des tests plus facilement sur plusieurs fichiers.

Lors de la construction du fichier .sln généré (dans Visual C++ 2010 Express), j’ai malheureusement une longue liste d’erreurs de la forme.

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)

ce qui, je pense, signifie que je ne parviens pas à créer de lien avec les bibliothèques gtest. Je me suis assuré que lors de la liaison avec les bibliothèques de débogage, j'ai ensuite essayé de construire en mode débogage.

[~ # ~] éditer [~ # ~]

Après avoir creusé un peu plus, je pense que mon problème est lié au type de bibliothèque dans laquelle je construis gtest. Lors de la construction de gtest avec CMake, si BUILD_SHARED_LIBS n'est pas cochée, et je lie mon programme à ces fichiers .lib, j'obtiens les erreurs mentionnées ci-dessus. Toutefois, si BUILD_SHARED_LIBS est cochée, puis je produis un ensemble de fichiers .lib et .dll. Lorsque le lien est établi avec ces fichiers .lib, le programme est compilé, mais lorsqu'il est exécuté, il se plaint de ne pas trouver gtest.dll.

Quelles sont les différences entre une bibliothèque SHARED et une bibliothèque non SHARED, et si je choisis de ne pas partager, pourquoi cela ne fonctionne-t-il pas? Existe-t-il une option dans le fichier CMakeLists.txt pour mon projet qui me manque?

110
Chris

La solution impliquait de placer le répertoire source gtest en tant que sous-répertoire de votre projet. J'ai inclus le fichier CMakeLists.txt de travail ci-dessous s'il est utile à quiconque.

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 )
69
Chris

Voici un exemple de travail complet que je viens de tester. Il télécharge directement à partir du Web, soit une archive corrigée, soit le dernier répertoire Subversion.

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)
28
user1427799

Très probablement, la différence dans les options du compilateur entre votre binaire de test et la bibliothèque de Google Test est à l'origine de telles erreurs. C'est pourquoi il est recommandé de placer Google Test dans le formulaire source et de le construire avec vos tests. C'est très facile à faire dans CMake. Vous venez d'appeler ADD_SUBDIRECTORY Avec le chemin d'accès à la racine du gtest, puis vous pouvez utiliser les cibles de bibliothèques publiques (gtest et gtest_main) Définies ici. Il y a plus d'informations de base dans ce fil CMake dans le groupe googletestframework.

[edit] L'option BUILD_SHARED_LIBS n'est effective que sous Windows pour le moment. Il spécifie le type de bibliothèques que vous souhaitez que CMake construise. Si vous le définissez sur ON, CMake les construira sous forme de DLL plutôt que de bibliothèque statique. Dans ce cas, vous devez créer vos tests avec -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 et copier les fichiers DLL générés par CMake dans le répertoire contenant votre binaire de test (CMake les place dans un répertoire de sortie distinct par défaut). À moins que gtest dans la bibliothèque statique ne fonctionne pas pour vous, il est plus facile de ne pas définir cette option.

13
VladLosev

Vous pouvez obtenir le meilleur des deux mondes. Il est possible d'utiliser ExternalProject pour télécharger la source gtest, puis d'utiliser add_subdirectory() pour l'ajouter à votre construction. Cela présente les avantages suivants:

  • gtest est construit dans le cadre de votre construction principale, il utilise donc les mêmes drapeaux de compilateur, etc., et évite donc des problèmes tels que ceux décrits dans la question.
  • Il n'est pas nécessaire d'ajouter les sources gtest à votre propre arborescence.

Utilisé normalement, ExternalProject ne se charge pas du téléchargement et de la décompression au moment de la configuration (c'est-à-dire lorsque CMake est exécuté), mais vous pouvez le faire avec un peu de travail. J'ai écrit un article de blog sur la façon de procéder, qui inclut également une implémentation généralisée qui fonctionne pour tout projet externe qui utilise CMake comme système de construction, pas seulement gtest. Vous pouvez les trouver ici:

Mise à jour: Cette approche est maintenant également partie de la documentation googletest .

10
Craig Scott

Après avoir creusé un peu plus, je pense que mon problème a quelque chose à voir avec le type de bibliothèque dans laquelle je construis gtest. Lors de la construction de gtest avec CMake, si BUILD_SHARED_LIBS n'est pas coché, et si je lie mon programme à ces fichiers .lib, les erreurs mentionnées ci-dessus sont résolues. Cependant, si BUILD_SHARED_LIBS est coché, je produis un ensemble de fichiers .lib et .dll. Lorsque le lien est établi avec ces fichiers .lib, le programme est compilé, mais lorsqu'il est exécuté, il se plaint de ne pas trouver gtest.dll.

En effet, vous devez ajouter -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 aux définitions du compilateur de votre projet si vous souhaitez utiliser gtest en tant que bibliothèque partagée.

Vous pouvez également utiliser les bibliothèques statiques, à condition de l'avoir compilé avec l'option gtest_force_shared_crt pour éliminer les erreurs que vous avez vues.

J'aime la bibliothèque, mais l'ajouter au projet est vraiment pénible. Et vous n’avez aucune chance de le faire correctement à moins de creuser (et de pirater) les fichiers gtest cmake. La honte. En particulier, je n'aime pas l'idée d'ajouter gtest comme source. :)

2
Slava

Le CMakeLists.txt le plus simple que j'ai extrait des réponses à ce fil et quelques essais et erreurs est:

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 devrait déjà être installé sur votre système.

0
AlexBriskin

Vos solutions et celles de VladLosevs sont probablement meilleures que les miennes. Si vous voulez une solution de force brute, essayez ceci:

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
Torleif