Bagaimana cara mulai bekerja dengan GTest dan CMake

125

Saya baru-baru ini dijual menggunakan CMake untuk menyusun proyek C ++ saya, dan sekarang ingin mulai menulis beberapa pengujian unit untuk kode saya. Saya telah memutuskan untuk menggunakan utilitas Google Test untuk membantu hal ini, tetapi memerlukan bantuan untuk memulai.

Sepanjang hari saya telah membaca berbagai panduan dan contoh termasuk Primer , pengenalan di IBM dan beberapa pertanyaan tentang SO (di sini dan di sini ) serta sumber-sumber lain yang saya lupa. Saya menyadari ada banyak hal di luar sana tetapi entah bagaimana saya masih mengalami kesulitan.

Saat ini saya mencoba menerapkan pengujian paling dasar, untuk mengonfirmasi bahwa saya telah mengumpulkan / menginstal hak gtest dan tidak berfungsi. Satu-satunya file sumber (testgtest.cpp) diambil hampir persis dari jawaban sebelumnya ini :

#include <iostream>

#include "gtest/gtest.h"

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

dan CMakeLists.txt saya yang terkait adalah sebagai berikut:

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
)

Perhatikan bahwa saya telah memilih untuk menautkan ke gtest_main daripada memberikan yang utama di akhir file cpp karena saya yakin ini akan memungkinkan saya untuk meningkatkan pengujian dengan lebih mudah ke beberapa file.

Saat membangun file .sln yang dihasilkan (dalam Visual C ++ 2010 Express) sayangnya saya mendapatkan daftar panjang kesalahan formulir

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)

yang menurut saya berarti saya tidak berhasil menautkan ke pustaka paling besar. Saya telah memastikan bahwa saat menautkan ke pustaka debug, saya kemudian mencoba membangun dalam mode debug.

EDIT

Setelah melakukan penggalian lagi, saya pikir masalah saya ada hubungannya dengan jenis perpustakaan yang saya bangun. Saat membangun gtest dengan CMake, jika BUILD_SHARED_LIBStidak dicentang, dan saya menautkan program saya ke file .lib ini, saya mendapatkan kesalahan yang disebutkan di atas. Namun, jika BUILD_SHARED_LIBSdicentang maka saya menghasilkan satu set file .lib dan .dll. Ketika sekarang menautkan file .lib ini, program mengkompilasi, tetapi ketika dijalankan mengeluh bahwa ia tidak dapat menemukan gtest.dll.

Apa perbedaan antara a SHAREDdan a not SHAREDlibrary, dan jika saya memilih tidak dibagikan, mengapa tidak berfungsi? Apakah ada opsi di CMakeLists.txt untuk proyek saya yang saya lewatkan?

Chris
sumber
4
Anda dapat menghindari menyertakan sumber GTest sendiri dengan menggunakan ExternalProject_Adddaripada add_subdirectory. Lihat jawaban ini untuk detailnya.
Fraser
Mengapa kita memiliki akses ke $ {gtest_SOURCE_DIR} pada contoh solusi di atas? Bagaimana / di mana variabel itu dideklarasikan?
dmonopoly
Oh, itu dideklarasikan di gtest-1.6.0 / CMakeLists.txt: "project (gtest CXX C)" yang membuat variabel gtest_SOURCE_DIR dan gtest_BINARY_DIR tersedia.
dmonopoly
1
Apa yang enable_testing()dilakukannya?
updogliu
1
@updogliu: Ini mengaktifkan ctest dan target 'test' (atau 'RUN_TESTS'). Itu bermain bersama dengan perintah add_test () cmake.
Ela782

Jawaban:

76

Solusinya melibatkan penempatan direktori sumber paling gt sebagai subdirektori proyek Anda. Saya telah menyertakan CMakeLists.txt yang berfungsi di bawah ini jika berguna bagi siapa pun.

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 )
Chris
sumber
3
Saya tidak yakin apa yang dilakukan add_test (), tetapi tampaknya tidak menghasilkan biner pengujian yang berjalan ... Apakah saya melewatkan sesuatu?
weberc2
4
Bukan untuk mengalahkan kuda mati tetapi saya pikir ini layak untuk disebutkan lagi. Komentar Fraser di atas membuat poin yang sangat penting: "Anda dapat menghindari menyertakan sumber GTest di Anda sendiri dengan menggunakan ExternalProject_Add daripada add_subdirectory." Lihat jawaban dan komentar Fraser untuk detailnya di sini: stackoverflow.com/a/9695234/1735836
Patricia
1
Dalam kasus saya, saya juga perlu menambahkan pthreadke pustaka tertaut, mengubah baris kedua terakhir menjaditarget_link_libraries(runUnitTests gtest gtest_main pthread)
panmari
3
@ weberc2 Anda harus menjalankan make testuntuk menjalankan pengujian, atau menjalankan ctestdari direktori build. Jalankan ctest -Vuntuk melihat keluaran pengujian google serta ctestkeluarannya.
Patrick
38

Berikut adalah contoh kerja lengkap yang baru saja saya uji. Ini mengunduh langsung dari web, baik tarball tetap, atau direktori subversi terbaru.

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)
pengguna1427799
sumber
7
Saya tidak tahu mengapa Anda mendapat suara negatif untuk ini. Solusi Anda mencegah seseorang dari keharusan memeriksa Google Test ke kontrol versi. Kudos untuk solusi Anda.
Sal
4
URL yang Anda gunakan sekarang rusak. URL terbaru adalahhttps://github.com/google/googletest/archive/release-1.8.0.zip
oscfri
Jawaban yang bagus. Seharusnya nomor 1.
Mr00Anderson
1
jawaban yang bagus! juga bisa kita gunakan GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.1sebagai pengganti URL
TingQian LI
URL ke rilis terbaru paling https://github.com/google/googletest/archive/release-1.10.0.zip
gt
16

Anda bisa mendapatkan yang terbaik dari kedua dunia. Hal ini dapat digunakan ExternalProjectuntuk mendownload sumber gtest dan kemudian digunakan add_subdirectory()untuk menambahkannya ke build Anda. Ini memiliki keuntungan sebagai berikut:

  • gtest dibuat sebagai bagian dari build utama Anda, jadi ia menggunakan tanda compiler yang sama, dll. dan karenanya menghindari masalah seperti yang dijelaskan dalam pertanyaan.
  • Tidak perlu menambahkan sumber paling gt ke pohon sumber Anda sendiri.

Digunakan dengan cara biasa, ExternalProject tidak akan mengunduh dan membongkar pada waktu konfigurasi (yaitu ketika CMake dijalankan), tetapi Anda bisa melakukannya hanya dengan sedikit kerja. Saya telah menulis posting blog tentang cara melakukan ini yang juga mencakup implementasi umum yang berfungsi untuk proyek eksternal apa pun yang menggunakan CMake sebagai sistem build-nya, bukan hanya gtest. Anda dapat menemukannya di sini:

Pembaruan: Pendekatan ini sekarang juga menjadi bagian dari dokumentasi googletest .

Craig Scott
sumber
2
IMO, ini mungkin cara terbersih untuk mengimplementasikan pengujian Google dengan proyek CMake. Saya berharap moderator lebih memperhatikan konten dan kualitas jawaban.
NameRakes
Modul umum DownloadProject.cmake yang ditautkan sangat bagus. Rasanya seperti dasar untuk cmake yang memiliki sistem manajemen paket di mana yang saya butuhkan hanyalah daftar tautan ke url github yang kompatibel dengan CMake.
Josh Peak
13

Kemungkinan besar, perbedaan opsi compiler antara biner pengujian Anda dan pustaka Google Test adalah penyebab kesalahan tersebut. Itulah mengapa disarankan untuk membawa Google Test dalam bentuk sumber dan membuatnya bersama dengan pengujian Anda. Sangat mudah dilakukan di CMake. Anda baru saja memanggil ADD_SUBDIRECTORYdengan jalur ke root paling gt dan kemudian Anda dapat menggunakan target perpustakaan umum ( gtestdan gtest_main) yang ditentukan di sana. Ada lebih banyak informasi latar belakang di utas CMake ini di grup googletestframework.

[Sunting] BUILD_SHARED_LIBSOpsi ini hanya efektif pada Windows untuk saat ini. Ini menentukan jenis pustaka yang Anda inginkan untuk dibuat oleh CMake. Jika Anda menyetelnya ke ON, CMake akan membuatnya sebagai DLL, bukan sebagai lib statis. Dalam hal ini Anda harus membangun pengujian Anda dengan -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 dan menyalin file DLL yang dihasilkan oleh CMake ke direktori dengan biner pengujian Anda (CMake menempatkannya di direktori output terpisah secara default). Kecuali gtest di lib statis tidak berfungsi untuk Anda, lebih mudah untuk tidak menyetel opsi itu.

VladLosev
sumber
1
Terima kasih banyak, tidak menyadari bahwa Anda dapat membangun proyek yang benar-benar terpisah dalam CMakeLists yang sama seperti itu. Sekarang saya dapat dengan aman mengatakan bahwa EXPECT_EQ (1.0 == 1.0) lolos dan EXPECT_EQ (0.0 == 1.0) gagal. Sekarang waktunya untuk tes yang lebih nyata ...
Chris
2

Setelah melakukan penggalian lagi, saya pikir masalah saya ada hubungannya dengan jenis perpustakaan yang saya bangun. Saat membangun gtest dengan CMake, jika BUILD_SHARED_LIBS tidak dicentang, dan saya menautkan program saya ke file .lib ini, saya mendapatkan kesalahan yang disebutkan di atas. Namun, jika BUILD_SHARED_LIBS dicentang maka saya menghasilkan satu set file .lib dan .dll. Ketika sekarang menautkan file .lib ini, program mengkompilasi, tetapi ketika dijalankan mengeluh bahwa ia tidak dapat menemukan gtest.dll.

Itu karena Anda harus menambahkan -DGTEST_LINKED_AS_SHARED_LIBRARY = 1 ke definisi kompiler dalam proyek Anda jika Anda ingin menggunakan gtest sebagai pustaka bersama.

Anda juga dapat menggunakan pustaka statis, asalkan Anda mengompilasinya dengan opsi gtest_force_shared_crt untuk menghilangkan kesalahan yang Anda lihat.

Saya suka perpustakaan tetapi menambahkannya ke proyek benar-benar menyakitkan. Dan Anda tidak memiliki kesempatan untuk melakukannya dengan benar kecuali Anda menggali (dan meretas) ke dalam file cmake paling bagus. Malu. Secara khusus saya tidak suka ide menambahkan gtest sebagai sumber. :)

Slava
sumber
1

OP menggunakan Windows, dan cara yang lebih mudah untuk menggunakan GTest saat ini adalah dengan vcpkg + cmake.


Instal vcpkg sesuai https://github.com/microsoft/vcpkg , dan pastikan Anda dapat menjalankan vcpkgdari baris cmd. Catat folder instalasi vcpkg, mis. C:\bin\programs\vcpkg.

Instal gtest menggunakan vcpkg install gtest: ini akan mengunduh, mengkompilasi, dan menginstal GTest.

Gunakan CmakeLists.txt seperti di bawah ini: perhatikan kita dapat menggunakan target daripada menyertakan folder.

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)

Jalankan cmake dengan: (edit folder vcpkg jika perlu, dan pastikan jalur ke file toolchain vcpkg.cmake sudah benar)

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

dan membangun menggunakan cmake --build buildseperti biasa. Perhatikan bahwa, vcpkg juga akan menyalin gtest (d) .dll / gtest (d) _main.dll yang diperlukan dari folder instal ke folder Debug / Rilis.

Uji dengan cd build & ctest.

Daniele
sumber
0

Solusi Anda dan VladLosevs mungkin lebih baik daripada solusi saya. Namun, jika Anda menginginkan solusi kekerasan, coba ini:

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)
Torleif
sumber
0

CMakeLists.txt paling sederhana yang saya saring dari jawaban di utas ini dan beberapa trial and error adalah:

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 seharusnya sudah diinstal di sistem Anda.

AlexBriskin
sumber
Benar-benar bukan praktik yang baik untuk menambahkan pustaka seperti ini di CMake. Salah satu tujuan utama cmake adalah tidak perlu membuat asumsi seperti "Libs ini harus sudah diinstal ...". CMake memeriksa bahwa perpustakaan ada di sini, dan jika tidak, kesalahan dilemparkan.
Adrien BARRAL
0

Sama seperti pembaruan untuk komentar @ Patricia dalam jawaban yang diterima dan komentar @ Fraser untuk pertanyaan asli, jika Anda memiliki akses ke CMake 3.11+, Anda dapat menggunakan fungsi FetchContent CMake .

Halaman FetchContent CMake menggunakan googletest sebagai contoh!

Saya telah memberikan sedikit modifikasi dari jawaban yang diterima:

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)

Anda dapat menggunakan INTERFACE_SYSTEM_INCLUDE_DIRECTORIESproperti target dari target gtest dan gtest_main seperti yang ditetapkan di skrip CMakeLists.txt pengujian google .

Mr Splat
sumber
Dalam CMake> = v3.14 Anda dapat mengabaikan eksplisit target_include_directoriesdan menggunakan FetchContent_MakeAvailable(googletest)sebagai gantinya. Ini akan mengisi konten dan menambahkannya ke build utama. CMake FetchContent - info lebih lanjut
67hz
0

Saya memutuskan untuk menyatukan sesuatu yang generik dengan sangat cepat yang menunjukkan cara yang berbeda untuk melakukannya daripada jawaban yang diposting sebelumnya, dengan harapan itu dapat membantu seseorang. Berikut ini bekerja untuk saya di Mac saya. Pertama saya menjalankan perintah pengaturan untuk gtests. Saya baru saja menggunakan skrip yang saya temukan untuk mengatur semuanya.

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

Selanjutnya, saya membuat struktur folder sederhana dan menulis beberapa kelas cepat

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

Saya membuat CMakeLists.txt tingkat atas untuk folder utils, dan CMakeLists.txt untuk folder tes

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)

Ini adalah CMakeLists.txt di folder tes

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

Kemudian yang tersisa hanyalah menulis contoh gtest dan gtest main

sample 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

sample gtest main

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

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

Saya kemudian dapat mengkompilasi dan menjalankan gtests dengan perintah berikut dari folder utils

cmake .
make 
./tests/gtestProject
ShowLove
sumber