栞忘れてください
FROM THE NOTEBOOK

GTest 单元测试模板

4 min read

项目架构#

假定项目的结构为:

.
├── build
├── CMakeLists.txt
├── Makefile
├── README.md
├── scripts
├── src
│   └── main.cpp
└── tests
    ├── CMakeLists.txt
    └── tree.cpp

我们在 tests 中写单元测试,其中,一个 cpp 文件是一个大的单元测试

CMakeLists#

我们在根目录的 CMakeLists.txt 的最后写上:

...
enable_testing()
add_subdirectory(tests)

然后,在 tests/CMakeLists.txt 中写上:

# Find required packages
find_package(GTest REQUIRED)

# Find all test source files
file(GLOB TEST_SOURCES "*.cpp")

# Create Library to Test
...

# For each test file, create a separate test executable
foreach(TEST_SOURCE ${TEST_SOURCES})
    get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE)

    add_executable(${TEST_NAME}_test ${TEST_SOURCE})

    target_link_libraries(${TEST_NAME}_test
	    ${CUSTOM_LIBS}
    )

    # Add to CTest
    add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}_test)
endforeach()

然后我们在根目录的 Makefile 下创建一个 test 目标:

.PHONY: all build test

# Default target
all: build

build:
	@cmake -S . -B build
	@cmake --build build -- -j$(shell nproc)

# Run tests
test: build
	@cd build && ctest --output-on-failure

# Run specific test by name
test-%: build
	@cd build && ctest -R $* --output-on-failure

# Run tests with verbose output
test-verbose: build
	@cd build && ctest -V

这样,我们写完测试后即可通过运行

make test

做单元测试

单元测试模板#

这里我们通过 TestFixture 的方式来写单元测试,例如现在我有一个二叉树 PartitionTree,那么我们的单元测试如下:

#include "partition_tree.hpp"
#include <gtest/gtest.h>
#include <sys/types.h>

class PartitionTreeTest : public ::testing::Test {
public:
  PartitionTree tree;

  std::string debug_message() {
    std::stringstream ss;
    return tree.to_string();
  }

protected:
  void SetUp() override { tree.init(n_threads); }

  void TearDown() override { tree.clear(); }

  std::vector<int> assumptions = {1, 2, 3, 4, 5, 6, 7};
  int depth = 3;
  int n_threads = 8;
};

TEST_F(PartitionTreeTest, BasicInitializationBinMode) {
  tree.init_with_binary(assumptions, depth, n_threads);
  EXPECT_TRUE(tree.m_root != nullptr);
  for (int i = 0; i < n_threads; i++) {
    EXPECT_TRUE((int)tree.collect_pre_decision(i).size() == depth);
  }
  EXPECT_TRUE(
      reinterpret_cast<Node *>(tree.m_wid_to_addr[0])->m_parent->m_split_lit ==
      3);
}

TEST_F(PartitionTreeTest, BasicInitializationScaMode) {

  tree.init_with_scattering(assumptions, 2);

  auto w0_pd = tree.collect_pre_decision(0);
  auto w1_pd = tree.collect_pre_decision(1);
  EXPECT_TRUE(w0_pd.size() == 1 && w1_pd.size() == 1);
  EXPECT_TRUE(w0_pd[0] == 1 && w1_pd[0] == -1) << debug_message();
}

TEST_F(PartitionTreeTest, BasicUpdateInBinMode) {
  tree.init_with_binary(assumptions, depth, n_threads);

  u_int64_t w1_addr = tree.m_wid_to_addr[1];

  tree.update(0, 1, 9);

  u_int64_t w1_addr_new = tree.m_wid_to_addr[1];

  EXPECT_TRUE(w1_addr != w1_addr_new);

  auto w0_pd = tree.collect_pre_decision(0);
  auto w1_pd = tree.collect_pre_decision(1);

  EXPECT_TRUE(w0_pd.size() == 4);
  EXPECT_TRUE(w1_pd.size() == 4);

  EXPECT_TRUE(w0_pd.back() == -9);
  EXPECT_TRUE(w1_pd.back() == 9);
}

TEST_F(PartitionTreeTest, BasicUpdateInScaMode) {
  tree.init_with_scattering(assumptions, n_threads);

  u_int64_t w1_addr = tree.m_wid_to_addr[1];

  tree.update(0, 1, 9);

  u_int64_t w1_addr_new = tree.m_wid_to_addr[1];

  EXPECT_TRUE(w1_addr != w1_addr_new) << debug_message();

  auto w0_pd = tree.collect_pre_decision(0);
  auto w1_pd = tree.collect_pre_decision(1);

  EXPECT_TRUE(w0_pd.size() == 2);
  EXPECT_TRUE(w1_pd.size() == 2);

  EXPECT_TRUE(w0_pd.back() == -9);
  EXPECT_TRUE(w1_pd.back() == 9);
}

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

这个文件是一个大测试点,其中,每个小测试点 Test Case 通过 TEST_F(TestFixure, TestCaseName) 实现,我们这么写的原因是为了可以共享一些测试变量,也可以少写一些代码(

CONVERSATION

讨论

想法、补充,或只是打个招呼。

← 返回文章
关系图谱 ↓

全局关系图谱

页面标签
笔记
↑ ↓ 选择↵ 打开Esc 关闭