注意:
GTest 的 github 地址: https://github.com/google/googletest
为了稳定性, 一般不直接拉取github 上面的代码, 而是下载 release 标签下的源码压缩包.
当前我看到的最新 release 版本是: 1.8.1.
所以下面的命令是 下载
以及 编译
和 安装
的过程.
# 进入到下载目录
cd ~/Downloads
# 下载最新的release 并保存为 googletest-1.8.1.tar.gz
wget -O googletest-1.8.1.tar.gz https://github.com/google/googletest/archive/release-1.8.1.tar.gz
# 解压
tar -zxf googletest-1.8.1.tar.gz
# 进入解压后的目录
cd googletest-release-1.8.1
# 为编译创建一个目录
mkdir build && cd build
cmake ..
make
# 安装
sudo make install
头文件安装到了 /usr/local/include/gtest/
库文件在 /usr/local/lib/
下, 它们分别是: libgtest.a
和 libgtest_main.a
还有一些cmake 的模块文件被安装到了你的 cmake 模块目录下.
网上大部分博客都没有给出一个真正能让人弄懂或符合实际项目的示例.
我最先掌握的单元测试是 Java 中的 Maven + JUnit 方式的测试,
我认为大多数的单元测试都应该是那样的目录结构
.
所以本示例的项目目录结构如下
├── CMakeLists.txt
├── main.cpp
├── src
│ └── myproject
│ └── myclass.hpp
└── test
├── CMakeLists.txt
└── test_my_class.cpp
我这里将 项目根目录
描述为 /
目录.
/src
为程序源码目录
/test
是测试代码目录
文件 /src/myproject/myclass.hpp
的内容为:
#pragma once
#include
class my_class
{
public:
my_class(const std::string& name, int age)
{
m_age = age;
m_name = name;
}
public:
int get_age() { return m_age; }
std::string get_name() { return m_name; }
private:
int m_age;
std::string m_name;
};
文件 /test/test_my_class.cpp
的内容为:
#include
#include
TEST(test_my_class, get_age)
{
my_class myClass("Joel", 21);
ASSERT_TRUE(myClass.get_age() == 16) << "age is not 16";
}
TEST(test_my_class, get_name)
{
my_class myClass("Joel", 21);
ASSERT_EQ(myClass.get_name(), "Joel") << "name is not Joel";
}
文件 /test/CMakeLists.txt
的内容为:
# 查找 GTest 库
find_package(GTest REQUIRED)
# GTest 的头文件
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(test_my_class test_my_class.cpp)
# 链接测试库
target_link_libraries( test_my_class
${GTEST_BOTH_LIBRARIES}
pthread )
# 添加到测试
gtest_discover_tests(test_my_class)
注意:
target_link_libraries()
里面 link 了 ${GTEST_BOTH_LIBRARIES}
, 这个表示链接google test 的两个 库.
GTest 依赖 pthread 库, 所以要链接.
特别提示: pthread 库一定要写在 ${GTEST_BOTH_LIBRARIES}
的后面, 否则编译时会报错,
错误示例如下:
undefined reference to `pthread_getspecific'
undefined reference to `pthread_key_delete'
最初我还以为我没链接 pthread 库, 可是我明明写了的啊 ! 搞了好久才找到问题.
文件 /CMakeLists.txt
的内容为:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
include_directories(${PROJECT_SOURCE_DIR}/src)
# 开启测试
enable_testing()
set(MY_PROJECT_SRC
src/myproject/myclass.hpp)
add_executable(MyProject
${MY_PROJECT_SRC}
main.cpp )
# 添加测试目录
add_subdirectory(test)
我们在项目的根木下新建一个文件夹 build
然后编译:
mkdir build && cd build
cmake ..
以下是 cmake 的输出:
-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found GTest: /usr/local/lib/libgtest.a
-- Configuring done
-- Generating done
从倒数第三行可以看到, cmake 已经找到了 GTest 的库文件
然后进行编译
make
cmake 的输出如下:
Scanning dependencies of target MyProject
[ 25%] Building CXX object CMakeFiles/MyProject.dir/main.cpp.o
[ 50%] Linking CXX executable MyProject
[ 50%] Built target MyProject
Scanning dependencies of target test_my_class
[ 75%] Building CXX object test/CMakeFiles/test_my_class.dir/test_my_class.cpp.o
[100%] Linking CXX executable test_my_class
[100%] Built target test_my_class
可以看到, 一共生成了两个文件, 分别是 程序目标文件
(MyProject) 和 测试目标文件
(test_my_class).
其中 test_my_class
文件在 当前目录下的 test/
文件夹下, 我们运行它, 看看测试效果.
cd test
./test_my_class
输出结果:
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from test_my_class
[ RUN ] test_my_class.get_age
/home/joel/cpp/MyProject/test/test_my_class.cpp:9: Failure
Value of: myClass.get_age() == 16
Actual: false
Expected: true
age is not 16
[ FAILED ] test_my_class.get_age (0 ms)
[ RUN ] test_my_class.get_name
[ OK ] test_my_class.get_name (0 ms)
[----------] 2 tests from test_my_class (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
[ FAILED ] 1 test, listed below:
[ FAILED ] test_my_class.get_age
1 FAILED TEST
可以看到一个测试成功一个测试失败, 达到我们的目的了.
上面的示例可以说是非常的简单了:
当程序结构越来越复杂的时候, 像 /test/CMakeLists.txt 这样的文件写起来就会变得相对的复杂.
当然, 有了这篇博客作为基石, 能运行起来最简单的测试代码之后害怕更复杂的吗?
万事开头难, 网上的教程并不一定符合你的理解能力或者项目实际情况.