CMake项目使用ctest+gtest进行单元测试

这篇具有很好参考价值的文章主要介绍了CMake项目使用ctest+gtest进行单元测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

随着CMake工具越来越强大便捷,越来越多的C/C++项目转而使用CMake来进行编译管理,它还提供了用于测试的ctest命令来执行项目中编写的单元测试。

本文就以一个实例来介绍如何使用ctest来进行单元测试。

一、环境准备

本文实例环境VSCode+MinGW64+CMake+gtest。

需要在MinGW中安装gtest,如果没有安装也没有关系,在CMakeLists.txt中进行检测,如果发现没有安装,则自动下载源码进行编译。

二、新建项目

新建一个目录,比如demo,然后使用VSCode打开目录,创建一个CMake项目。
创建CMake项目可以使用VSCode的CMake向导来创建,也可以直接在目录中编写一个CMakeLists.txt来创建。

使用VSCode的CMake向导创建项目

在VSCode中按F1,在弹出的选项中选择CMake:快速入门

CMake项目使用ctest+gtest进行单元测试

然后选择编译套件,如果需要搜索,可以选择[Scan for kits]

CMake项目使用ctest+gtest进行单元测试

然后输入项目名称,比如demo

CMake项目使用ctest+gtest进行单元测试

根据项目需要选择库(Library)或者可执行体(Executable),这里选择Executable

CMake项目使用ctest+gtest进行单元测试

然后向导会自动新建CMakeLists.txt和main.cpp:

CMake项目使用ctest+gtest进行单元测试

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.0)
project(demo VERSION 0.1.0)

include(CTest)
enable_testing()

add_executable(demo main.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

Main.cpp:

#include <iostream>

int main(int, char**) {
    std::cout << "Hello, world!\n";
}

自行创建CMake项目

其实就是自己编写上面的CMakeLists.txt和项目源码

可以从CMakeLists.txt中看到已经添加并启用了CTest

三、创建单元测试

1、添加需要测试的功能,比如建立一个func.h和func.cpp

func.h

#ifndef __FUNC_H_INCLUDE_
#define __FUNC_H_INCLUDE_

int Factorial(int n);
bool IsPrime(int n);

#endif

func.cpp

#include "func.h"

// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n) {
  int result = 1;
  for (int i = 1; i <= n; i++) {
    result *= i;
  }

  return result;
}

// Returns true if and only if n is a prime number.
bool IsPrime(int n) {
  // Trivial case 1: small numbers
  if (n <= 1) return false;

  // Trivial case 2: even numbers
  if (n % 2 == 0) return n == 2;

  // Now, we have that n is odd and n >= 3.

  // Try to divide n by every odd number i, starting from 3
  for (int i = 3;; i += 2) {
    // We only have to try i up to the square root of n
    if (i > n / i) break;

    // Now, we have i <= n/i < n.
    // If n is divisible by i, n is not prime.
    if (n % i == 0) return false;
  }

  // n has no integer factor in the range (1, n), and thus is prime.
  return true;
}

2、创建测试目录test,并在目录中添加CMakeLists.txt、tmain.cpp和单元测试代码test.cpp。

test/CMakeLists.txt

add_executable(t tmain.cpp test.cpp ../func.cpp)
target_link_libraries(t PRIVATE gtest)

add_test(NAME t COMMAND t)

tmain.cpp

#include <gtest/gtest.h>

int main()
{
    testing::InitGoogleTest();
    return RUN_ALL_TESTS();
}

test.cpp

#include <gtest/gtest.h>
#include "../func.h"


// Tests Factorial().

// Tests factorial of negative numbers.
TEST(FactorialTest, Negative) {
  // This test is named "Negative", and belongs to the "FactorialTest"
  // test case.
  EXPECT_EQ(1, Factorial(-5));
  EXPECT_EQ(1, Factorial(-1));
  EXPECT_GT(Factorial(-10), 0);

  // <TechnicalDetails>
  //
  // EXPECT_EQ(expected, actual) is the same as
  //
  //   EXPECT_TRUE((expected) == (actual))
  //
  // except that it will print both the expected value and the actual
  // value when the assertion fails.  This is very helpful for
  // debugging.  Therefore in this case EXPECT_EQ is preferred.
  //
  // On the other hand, EXPECT_TRUE accepts any Boolean expression,
  // and is thus more general.
  //
  // </TechnicalDetails>
}

// Tests factorial of 0.
TEST(FactorialTest, Zero) { EXPECT_EQ(1, Factorial(0)); }

// Tests factorial of positive numbers.
TEST(FactorialTest, Positive) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
}

// Tests IsPrime()

// Tests negative input.
TEST(IsPrimeTest, Negative) {
  // This test belongs to the IsPrimeTest test case.

  EXPECT_FALSE(IsPrime(-1));
  EXPECT_FALSE(IsPrime(-2));
  EXPECT_FALSE(IsPrime(INT_MIN));
}

// Tests some trivial cases.
TEST(IsPrimeTest, Trivial) {
  EXPECT_FALSE(IsPrime(0));
  EXPECT_FALSE(IsPrime(1));
  EXPECT_TRUE(IsPrime(2));
  EXPECT_TRUE(IsPrime(3));
}

// Tests positive input.
TEST(IsPrimeTest, Positive) {
  EXPECT_FALSE(IsPrime(4));
  EXPECT_TRUE(IsPrime(5));
  EXPECT_FALSE(IsPrime(6));
  EXPECT_TRUE(IsPrime(23));
}

项目的目录结构如下:

CMake项目使用ctest+gtest进行单元测试

3、修改根目录CMakeLists.txt

需要在CMakeLists.txt中引用test目录,添加如下命令:

add_subdirectory(test)

4、运行测试

如果MinGW中安装有gtest则可以在VSCode中执行Run CTest了:

CMake项目使用ctest+gtest进行单元测试

输出如下:

CMake项目使用ctest+gtest进行单元测试

当然,也可以在VSCode中选择测试目标t直接运行测试:

CMake项目使用ctest+gtest进行单元测试

有没发现使用ctest并不能像直接运行测试目标t那样显示出详细的测试项目,那是因为在CMakeLists.txt中是使用的通用方法add_test(NAME t COMMAND t)添加的测试,其实CMake是直接支持gtest的,只需要把add_test(NAME t COMMAND t)换成下面两句即可:

include(GoogleTest)
gtest_add_tests(TARGET t)

可以看到各测试项目的情况了:

CMake项目使用ctest+gtest进行单元测试

而且运行Run CTest后,VSCode中也会提示有几个测试用例和通过情况:

CMake项目使用ctest+gtest进行单元测试

使用gtest_add_tests有一个问题:一旦测试用例改变,它就需要重新执行cmake,否则无法知道改变后的测试用例。 所以CMake添加了gtest_discover_tests指令,它通过调用编译后的执行程序并添加参数--gtest_list_tests来获取测试用例的,所以不需要重新执行CMake。

既然如此,那前面的tmain.cpp需要作修改以接收参数:

tmain.cpp

#include <gtest/gtest.h>

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

此时把gtest_add_tests(TARGET t)替换成gtest_discover_tests(t)即可,修改后的CMakeLists.txt完整源码:

add_executable(t tmain.cpp test.cpp ../func.cpp)
target_link_libraries(t PRIVATE gtest)

include(GoogleTest)
gtest_discover_tests(t)

其实,gtest本身是带有一个main函数库的,只需要包链接gtest_main即可,不需要自己写tmain.cpp中的内容。可以把tmain.cpp删除,使用下面的CMakeLists.txt即可:

add_executable(t test.cpp ../func.cpp)
target_link_libraries(t PRIVATE gtest gtest_main)

include(GoogleTest)
gtest_discover_tests(t)

四、让CMake自动下载、编译依赖

前面有提到,要运行示例,必须要求安装了gtest,可以写入CMakeLists.txt中,使用CMake的find_package命令来查找,本例是需要GTest,添加find_package(GTest REQUIRED),并且是必须要安装有,所有后面添加了REQUIRED参数,注意必须是大写。

如果找不到GTest则会报错:
CMake项目使用ctest+gtest进行单元测试
这种方式要求在MinGW中安装有GTest,可以使用MinGW命令:pacman -S mingw-w64-x86_64-gtest来安装。

当然更友好的方式是如果系统没有安装则自己下载源码进行编译引用,在根目录的CMakeLists.txt中添加:

cmake_policy(SET CMP0135 NEW)
find_package(GTest)
if(NOT GTest_FOUND)
message("GTest not found, download it...")
include(FetchContent)
FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/heads/main.zip)
FetchContent_MakeAvailable(googletest)
endif()

这里find_package没有添加REQUIRED参数来强制要求,只是检测,后面判断检测结果GTest_FOUND,如果没有找到则从指定URL下载(FetchContent_Declare)并编译(FetchContent_MakeAvailable),由于使用URL下载需要添加cmake_policy(SET CMP0135 NEW),不然会报警告:

[cmake]   The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
[cmake]   not set.  The policy's OLD behavior will be used.  When using a URL
[cmake]   download, the timestamps of extracted files should preferably be that of
[cmake]   the time of extraction, otherwise code that depends on the extracted
[cmake]   contents might not be rebuilt if the URL changes.  The OLD behavior
[cmake]   preserves the timestamps from the archive instead, but this is usually not
[cmake]   what you want.  Update your project to the NEW behavior or specify the
[cmake]   DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this
[cmake]   robustness issue.

FetchContent_Declare也可以使用GIT_REPOSITORY从Git克隆下来,但是这种方式如果网络不好则比较慢。

注意为了使用这些高级指令,最好是安装最新的CMake版本,FetchContent最低要求3.11:

cmake_minimum_required(VERSION 3.11.0)

写得非常详细,如果觉得对你有帮助,欢迎点赞收藏!文章来源地址https://www.toymoban.com/news/detail-448821.html

到了这里,关于CMake项目使用ctest+gtest进行单元测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Cmake】Ctest测试工具

    目录 前言 一、初识CTest 二、使用方法 三、完整的简单测试工程 附录 附录一 cmake命令 enable_testing add_test 原文:CTest - https://www.cnblogs.com/457220157-FTD/p/4139149.html        CTest是CMake集成的一个测试工具,在使用CMakeLists.txt文件编译工程的时候,CTest会自动configure、build、test和展现

    2024年02月07日
    浏览(35)
  • 使用gtest做单元测试

    gtest是一个跨平台的(Liunx、Mac OS X、Windows 、Cygwin 、Windows CE and Symbian ) C++单元测试框架,由google公司发布。gtest是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等 配置gtest头文件及库 下载gtest源码: Releases · goog

    2023年04月12日
    浏览(32)
  • 手把手教你使用gtest写单元测试

    开源框架:gtest,它主要用于写单元测试,检查真自己的程序是否符合预期行为。这不是QA(测试工程师)才学的,也是每个优秀后端开发codoer的必备技能。 本期博文内容及使用的demo,参考: Googletest Basic Guide[1] Googletest Samples [2] 构建依赖环境 按照惯例,先介绍下怎么基于

    2024年02月16日
    浏览(47)
  • 单元测试gtest的安装与使用方法【结合官网的sample】

    gtest单元测试是Google的一套用于编写 C++测试的框架 ,可以运行在很多平台上(包括Linux、Mac OS X、Windows、Cygwin等等)。基于xUnit架构。支持很多好用的特性,包括自动识别测试、丰富的断言、断言自定义、死亡测试、非终止的失败、生成XML报告等等。 好的测试应该有下面的这

    2024年02月10日
    浏览(37)
  • 在一个maven项目中使用maven命令进行junit单元测试

    如何在一个maven项目中使用maven命令进行junit单元测试? 首先确定一个maven项目的结构: 包含源代码目录src/main/java. 配置目录src/main/resources. 测试代码目录src/test. 目录结构可视化如下: 假定demo.java内容如下所示 我们要测试这个demo类, 可以在demoTest.java中写以下代码 然后就可以通过

    2023年04月21日
    浏览(53)
  • 在Java微服务项目中,如何使用Mock来进行单元测试?

    摘要: 在系统开发的过程中,单元测试是其中的一个重要环节。在Java微服务项目中,Spring框架本身就为我们提供了一套单元测试的框架SpringBootTest。如果我们在学校完成课堂作业或出于兴趣爱好自学,是可以使用Spring自带的单元测试框架进行单测的。 工作中,这种通过Spri

    2024年02月16日
    浏览(36)
  • gtest--单元测试

    gtest是Google的一套用于编写C++测试的框架,可以运行在很多平台上(包括Linux、Mac OS X、Windows、Cygwin等等)。基于xUnit架构。支持很多好用的特性,包括自动识别测试、丰富的断言、断言自定义、死亡测试、非终止的失败、生成XML报告等等。 好的测试应该有下面的这些特点,我

    2024年02月13日
    浏览(37)
  • gtest单元测试

    gtest是 Google 的一套用于 编写C++测试的框架 ,可以运行在很多平台上(包括Linux、Mac OS X、Windows、Cygwin等等)。基于xUnit架构。支持很多好用的特性,包括自动识别测试、丰富的断言、断言自定义、死亡测试、非终止的失败、生成XML报告等等。 测试应该是 独立的、可重复 的。

    2024年02月08日
    浏览(77)
  • 玩转单元测试之gtest

    程序开发的时候,往往需要编写一些测试样例来完成功能测试,以保证自己的代码在功能上符合预期,能考虑到一些异常边界问题等等。 1.引入gtest 2.编写第一个单测 2.1 待测试文件 2.2 单测文件 2.3 makefile文件 make ./hello_unit_test 编译并执行单测程序,执行结果如下: 1. 各种断

    2024年02月12日
    浏览(37)
  • C++单元测试Gtest+Stub攻略

    笔者环境为linux环境(deepin),以下均在此环境进行 Gtest源码链接 Stub源码链接 StubExt源码链接 Stub的使用方法在cpp-stub/README_zh.md中有讲解 StubExt的使用方法在 cpp-stub-ext/ README.md中有讲解 StubExt可支持Lambda表达式进行打桩写Gtest时如果想获取一个固定的返回值或者出参十分好用 搭建环

    2024年02月10日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包