GTest ve CMake ile çalışmaya nasıl başlanır


125

Son zamanlarda C ++ projelerimi derlemek için CMake kullanmaya başladım ve şimdi kodum için bazı birim testleri yazmaya başlamak istiyorum. Bu konuda yardımcı olması için Google Test yardımcı programını kullanmaya karar verdim, ancak başlamak için biraz yardıma ihtiyacım var.

Tüm gün boyunca çeşitli kılavuzları ve örnekleri okudum Primer , IBM'de bir giriş ve SO ile ilgili bazı sorular ( burada ve burada ) ve izini kaybettiğim diğer kaynaklar. Orada çok şey olduğunun farkındayım ama bir şekilde hala zorluklar yaşıyorum.

Şu anda gtest'i doğru derlediğimi / kurduğumu ve çalışmadığını doğrulamak için en temel testi uygulamaya çalışıyorum. Tek kaynak dosya (testgtest.cpp) neredeyse tam olarak bu önceki cevaptan alınmıştır:

#include <iostream>

#include "gtest/gtest.h"

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

ve ilişkili CMakeLists.txt dosyam aşağıdaki gibidir:

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
)

Main'i cpp dosyasının sonunda sağlamak yerine gtest_main'e bağlamayı seçtiğime dikkat edin, çünkü bunun testi birden çok dosyaya daha kolay ölçeklendirmeme izin vereceğine inanıyorum.

Oluşturulan .sln dosyasını oluştururken (Visual C ++ 2010 Express'te) maalesef formda uzun bir hata listesi alıyorum

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)

Sanırım gtest kitaplıklarına başarılı bir şekilde bağlanamadığım anlamına geliyor. Hata ayıklama kitaplıklarına bağlanırken, daha sonra hata ayıklama modunda oluşturmaya çalıştığımdan emin oldum.

DÜZENLE

Biraz daha araştırma yaptıktan sonra, benim sorunumun gtest'i inşa ettiğim kütüphane türüyle ilgili olduğunu düşünüyorum. CMake ile gtest oluştururken, BUILD_SHARED_LIBSkontrol edilmemişse ve programımı bu .lib dosyalarına bağlarsam, yukarıda belirtilen hataları alıyorum. Ancak, BUILD_SHARED_LIBSişaretlenirse , bir dizi .lib ve .dll dosyası oluşturuyorum. Şimdi bu .lib dosyalarına bağlanırken program derler, ancak çalıştırıldığında gtest.dll'yi bulamadığından şikayet eder.

A SHAREDve a not SHAREDkitaplığı arasındaki farklar nelerdir ve paylaşılmamayı seçersem neden çalışmaz? CMakeLists.txt'de projem için eksik olduğum bir seçenek var mı?


4
Sen kullanarak kendi içinde GTEST kaynaklar dahil önleyebilirsiniz ExternalProject_Addziyade add_subdirectory. Ayrıntılar için bu yanıta bakın.
Fraser

Yukarıdaki çözüm örneğinde neden $ {gtest_SOURCE_DIR} için erişimimiz var? Bu değişken nasıl / nerede açıklanır?
dmonopoly

Oh, gtest-1.6.0 / CMakeLists.txt: gtest_SOURCE_DIR ve gtest_BINARY_DIR değişkenlerini kullanılabilir kılan "proje (gtest CXX C)" olarak bildirilmiştir.
dmonopoly

1
Ne yapar enable_testing()?
updogliu

1
@updogliu: ctest ve 'test' (veya 'RUN_TESTS') hedefini etkinleştirir. Add_test () cmake komutuyla birlikte oynar.
Ela782

Yanıtlar:


76

Çözüm, gtest kaynak dizinini projenizin bir alt dizini olarak yerleştirmeyi içeriyordu. Herhangi birine yardımcı olacaksa, çalışan CMakeLists.txt dosyasını aşağıya ekledim.

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 () işlevinin ne yaptığından emin değilim, ancak test ikili programının çalışmasına neden olmuyor ... Bir şey mi kaçırıyorum?
weberc2

4
Ölü bir atı dövmek değil ama bunun tekrar bahsetmeye değer olduğunu düşündüm. Fraser'ın yukarıdaki yorumu çok önemli bir noktaya işaret ediyor: "GTest kaynaklarını kendi başınıza add_subdirectory yerine ExternalProject_Add kullanarak dahil etmekten kurtulabilirsiniz." Ayrıntılar için Fraser'ın cevabına ve yorumlarına buradan bakın: stackoverflow.com/a/9695234/1735836
Patricia

1
Benim durumumda, pthreadbağlantılı kütüphanelere de eklemem gerekiyordu, ikinci son satırıtarget_link_libraries(runUnitTests gtest gtest_main pthread)
panmari

3
@ weberc2 make testTestleri çalıştırmak için veya ctestyapı dizininden çalıştırmak zorundasınız . ctest -VGoogle test çıktısını ve çıktısını görmek için çalıştırın ctest.
Patrick

38

İşte az önce test ettiğim eksiksiz bir çalışma örneği. Doğrudan web'den, sabit bir tarball'dan veya en son alt sürüm dizininden indirir.

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
Bunun için neden reddedildiğini bilmiyorum. Çözümünüz, birinin Google Test'i sürüm kontrolüne kaydettirmesini engelliyor. Çözümünüz için tebrikler.
Sal

4
Kullandığınız URL artık bozuk. Güncel bir URL:https://github.com/google/googletest/archive/release-1.8.0.zip
oscfri

Mükemmel cevap. 1 numara olmalı
Mr00Anderson

1
harika cevap! GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.1URL yerine de kullanabiliriz
TingQian LI

En son sürümün URL'si şu şekildedir:https://github.com/google/googletest/archive/release-1.10.0.zip
vahancho

16

Her iki dünyanın da en iyisini elde edebilirsiniz. ExternalProjectGtest kaynağını indirmek ve ardından add_subdirectory()bunu yapınıza eklemek için kullanmak mümkündür . Bunun aşağıdaki avantajları vardır:

  • gtest, ana yapınızın bir parçası olarak oluşturulmuştur, bu nedenle aynı derleyici bayraklarını vb. kullanır ve bu nedenle soruda açıklananlar gibi sorunları önler.
  • Gtest kaynaklarını kendi kaynak ağacınıza eklemenize gerek yoktur.

Normal şekilde kullanıldığında, ExternalProject yapılandırma sırasında (yani CMake çalıştırıldığında) indirme ve paket açma işlemini yapmaz, ancak bunu sadece biraz çalışarak yapabilirsiniz. Bunun nasıl yapılacağına dair bir blog yazısı yazdım; bu aynı zamanda, CMake'i kendi inşa sistemi olarak kullanan, sadece gtest değil, herhangi bir harici proje için çalışan genelleştirilmiş bir uygulama da içerir. Onları burada bulabilirsiniz:

Güncelleme: Bu yaklaşım artık aynı zamanda googletest belgelerinin de bir parçasıdır .


2
IMO, bu belki de Google testini bir CMake projesiyle uygulamanın en temiz yoludur. Moderatörlerin cevapların içeriğine ve kalitesine daha fazla dikkat etmesini diliyorum.
NameRakes

Bağlantılı DownloadProject.cmake genelleştirilmiş modülü harika. Tüm ihtiyacım olan CMake uyumlu github url'lerine bağlantıların bir listesi olan bir paket yönetim sistemine sahip olan cmake için temel oluşturuyor.
Josh Peak

13

Büyük olasılıkla, test ikili programınız ve Google Test kitaplığınız arasındaki derleyici seçeneklerindeki fark, bu tür hataları suçlamaktır. Bu nedenle, Google Test'i kaynak formda getirmeniz ve testlerinizle birlikte oluşturmanız önerilir. CMake'de yapmak çok kolay. Sadece ADD_SUBDIRECTORYgtest kökünün yolunu çağırırsınız ve sonra orada tanımlanan halk kütüphanesi hedeflerini ( gtestve gtest_main) kullanabilirsiniz . Googletestframework grubundaki bu CMake dizisinde daha fazla arka plan bilgisi var .

[düzenle] Bu BUILD_SHARED_LIBSseçenek şimdilik yalnızca Windows'ta etkilidir. CMake'in oluşturmasını istediğiniz kitaplık türlerini belirtir. Bunu olarak ayarlarsanız ON, CMake bunları statik kitaplıkların aksine DLL'ler olarak oluşturur. Bu durumda, testlerinizi -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 ile oluşturmanız ve CMake tarafından üretilen DLL dosyalarını test ikili dosyanızın bulunduğu dizine kopyalamanız gerekir (CMake bunları varsayılan olarak ayrı bir çıktı dizinine yerleştirir). Statik kitaptaki gtest sizin için çalışmadıkça, bu seçeneği ayarlamamak daha kolaydır.


1
Çok teşekkürler, bunun gibi aynı CMakeListleri içinde tamamen ayrı projeler oluşturabileceğinizi bilmiyordum. Şimdi güvenle EXPECT_EQ (1.0 == 1.0) geçtiğini ve EXPECT_EQ (0.0 == 1.0) başarısız olduğunu söyleyebilirim. Şimdi daha gerçek testler zamanı ...
Chris

2

Biraz daha araştırma yaptıktan sonra, benim sorunumun gtest'i inşa ettiğim kütüphane türüyle ilgili olduğunu düşünüyorum. CMake ile gtest oluştururken, BUILD_SHARED_LIBS işaretli değilse ve programımı bu .lib dosyalarına bağlarsam, yukarıda bahsedilen hataları alıyorum. Bununla birlikte, BUILD_SHARED_LIBS işaretlenirse, bir dizi .lib ve .dll dosyası oluştururum. Şimdi bu .lib dosyalarına bağlanırken program derler, ancak çalıştırıldığında gtest.dll'yi bulamadığından şikayet eder.

Bunun nedeni, gtest'i paylaşılan bir kitaplık olarak kullanmak istiyorsanız, projenizdeki derleyici tanımlarına -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 eklemeniz gerektiğidir.

Ayrıca, gördüğünüz hataları ortadan kaldırmak için gtest_force_shared_crt seçeneğiyle derlemeniz koşuluyla, statik kitaplıkları da kullanabilirsiniz.

Kütüphaneyi beğendim ama projeye eklemek gerçekten acı verici. Ve gtest cmake dosyalarını kazmadıkça (ve hacklemedikçe) bunu doğru yapma şansınız yok. Utanç. Özellikle gtest'i kaynak olarak ekleme fikrinden hoşlanmıyorum. :)


1

OP, Windows kullanıyor ve GTest'i bugün kullanmanın çok daha kolay bir yolu vcpkg + cmake ile.


Vcpkg'yi https://github.com/microsoft/vcpkg uyarınca yükleyin vcpkgve cmd satırından çalıştırabildiğinizden emin olun . Vcpkg kurulum klasörünü not edin, örn. C:\bin\programs\vcpkg.

Gtest'i kullanarak kurun vcpkg install gtest: bu, GTest'i indirir, derler ve kurar.

Aşağıdaki gibi bir CmakeLists.txt kullanın: klasörleri dahil etmek yerine hedefleri kullanabileceğimizi unutmayın .

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 komutunu çalıştırın: (gerekirse vcpkg klasörünü düzenleyin ve vcpkg.cmake araç zinciri dosyasının yolunun doğru olduğundan emin olun)

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

ve cmake --build buildher zamanki gibi kullanarak inşa edin . Vcpkg'nin gerekli gtest (d) .dll / gtest (d) _main.dll dosyasını yükleme klasöründen Debug / Release klasörlerine kopyalayacağını unutmayın.

İle test edin cd build & ctest.


0

Sizin ve VladLosev'lerin çözümleri muhtemelen benimkinden daha iyi. Bununla birlikte, kaba kuvvet çözümü istiyorsanız, şunu deneyin:

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

Bu konudaki cevaplardan damıttığım en basit CMakeLists.txt ve bazı deneme yanılma şudur:

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, sisteminize zaten yüklenmiş olmalıdır.


CMake'de böyle bir kitaplık eklemek gerçekten iyi bir uygulama değil. Cmake'nin ana hedeflerinden biri, asla "Bu kütüphaneler zaten kurulmuş olmalı ..." gibi varsayımlar yapmak zorunda kalmamaktır. CKitaplığın burada olduğunu kontrol edin, yoksa bir hata atılır.
Adrien BARRAL

0

@ Patricia'nın kabul edilen yanıttaki yorumunda ve @ Fraser'ın orijinal soru için yorumunda bir güncelleme olarak, CMake 3.11+ erişiminiz varsa CMake'nin FetchContent işlevini kullanabilirsiniz.

CMake'nin FetchContent sayfası örnek olarak googletest kullanıyor!

Kabul edilen yanıtta küçük bir değişiklik yaptım:

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 komut dosyasında ayarlandıkları şekilde gtest ve gtest_main hedeflerinin hedef özelliğini kullanabilirsiniz .


CMake> = v3.14'te açıktan vazgeçebilir target_include_directoriesve FetchContent_MakeAvailable(googletest)bunun yerine kullanabilirsiniz . Bu hem içeriği dolduracak hem de ana yapıya ekleyecektir. CMake FetchContent - daha fazla bilgi
67hz

0

Birine yardımcı olabileceği umuduyla, genel bir şeyi daha önce gönderilmiş yanıtlardan farklı bir yolla hızlı bir şekilde bir araya getirmeye karar verdim. Aşağıdakiler benim için Mac'imde çalıştı. Öncelikle gtestler için kurulum komutlarını çalıştırdım. Her şeyi kurmak için bulduğum bir komut dosyası kullandım.

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

Ardından, basit bir klasör yapısı yaptım ve bazı hızlı dersler yazdım

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

Utils klasörü için üst düzey bir CMakeLists.txt ve testler klasörü için bir CMakeLists.txt yaptım

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)

Bu, testler klasöründeki CMakeLists.txt dosyasıdır

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

Sonra geriye kalan tek şey örnek bir gtest ve gtest main yazmaktır.

örnek 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

örnek gtest main

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

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

Daha sonra utils klasöründen aşağıdaki komutlarla gtests derleyip çalıştırabilirim

cmake .
make 
./tests/gtestProject
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.