Bazel 与 gtest:构建一个最简单的测试驱动开发环境

Bazel 介绍

Google 自家的构建工具,相比 CMake 的优势,主要是多语言构建和相比 Make 语法有更好的可读性。最著名的使用 Bazel 的项目大概是 Tensorflow 吧。目前生态上还是不太好和 CMake 相提并论,毕竟下面的命令已经像魔法一样刻在 Unix 的世界里。

./configure
make
make install

安装 Bazel

Mac 用户当然是使用 brew 解决了:

brew install bazel

安装参考官网吧~

Hello Bazel

要使用 Bazel,首先在项目顶层目录创建一个名为 WORKSPACE 的文件。值得一提的是 Bazel 推崇的是 Monorepo ,建议的项目结构是把代码、测试代码放到一起。在 C/C++ 中,意味着更加推荐头文件、源码文件、测试文件都放到同一个目录下。如果按 includesrclibtest 拆分,在 Bazel 的设计下,反而会增加难度。

因此我们就创建一个 src 文件夹,并在该文件夹下建立 BUILD 文件。WORKSPACE 顾名思义是工作区,BUILD 就是具体的构建目标。

.
├── WORKSPACE
└── src
    ├── BUILD

src 下创建一个最简单的 C++ 文件:

#include 
using namespace std;

int main(int argc, char* argv[])
{
     
    cout << "hello, bazel" << endl;
  return 0;
}

这个代码会编译成可执行文件,而不是库文件,因此在 BUILD 中写入第一个构建目标:

cc_binary(
  name = "main",
  srcs = ["main.cpp"]
)

接下来,就可以使用 Bazel 进行构建了:

bazel build src:main

如果终端的工作目录在 src,可以省略 srcbazel build :main

INFO: Analyzed target //src:main (14 packages loaded, 105 targets configured).
INFO: Found 1 target...
Target //src:main up-to-date:
  bazel-bin/src/main
INFO: Elapsed time: 4.106s, Critical Path: 0.86s
INFO: 2 processes: 2 darwin-sandbox.
INFO: Build completed successfully, 5 total actions

之后就使用 bazel run src:main 运行构建好的程序:

INFO: Analyzed target //src:main (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:main up-to-date:
  bazel-bin/src/main
INFO: Elapsed time: 0.132s, Critical Path: 0.01s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
hello, bazel

Bazel 会生成几个目录,可以在 bazel-bin 中找到构建好的程序。

Google Test

Google Test 是 Google 开发的一套 C++ 测试框架,原生支持了 Bazel 构建。为了自动导入所需的依赖(一般只有支持的 Bazel 的项目才可以),我们在 WORKSPACE 中写入针对 google test 的依赖条目。

workspace(name = "com_googletest_driven")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

# Google Test
http_archive(
  name = "com_google_googletest",
  urls = ["https://github.com/google/googletest/archive/release-1.10.0.zip"],
  strip_prefix = "googletest-release-1.10.0",
  sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
)

workspace 那一行可以简单的理解为项目名称。load 用来发现本地路径和加载对应的符号规则,这里引入的 http_archive 用来引用和检索打包好的 Bazel 库。一旦检索到压缩的仓库,就会自动解压,里面包含的规则、构建目标都可以直接在项目中使用。

strip_prefix 就是解压后的文件夹名称,用来指定抽取的文件目录。sha256 对下载的内容进行校验,可以极大的增加依赖的可入性。在 Linux 中,可以使用 sha256sum 获得该项内容;Mac OSX 上使用的 shasum -a 256

可以将 curlshasum 组合起来获得所需要的 sha256 校验码:

curl -L https://github.com/google/googletest/archive/release-1.10.0.zip | shasum -a 256
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91  

由于 Google Test 原生支持了 Bazel,这样就已经集成了 Google Test。

单元测试

Bazel 原生支持 test 目标的构建,编写基于 Google Test 的测试非常简单,甚至都不需要添加测试启动的入口。

我们试着构建一个最简单的测试用例,新建一个库的构建目标,在 src 中的 BUILD 中新增下面内容:

cc_library(
  name = "add",
  hdrs = ["add.hpp"],
  srcs = ["add.cpp"]
)

hdrs 显然就是公开的头文件。
示例代码的内容:

// add.hpp
#pragma once

int add(int a, int b);
// add.cpp
#include "add.hpp"

int add(int a, int b) {
     
  return a + b;
}

再创建一个 add_test.cpp 文件,引入 google test:

#include "gtest/gtest.h"

#include "add.hpp"
 
TEST(testAdd, test0)
{
     
  EXPECT_EQ(15, add(5, 10));
}

为了构建测试,在 BUILD 中新增测试目标:

cc_test(
  name = "test_add",
  srcs = ["hello_test.cpp"],
  deps = [
    ":add",
    "@com_google_googletest//:gtest_main",
  ],
)

deps 指明了依赖的代码和 google test 库。导入外部依赖库的规则是这样的:
Bazel 与 gtest:构建一个最简单的测试驱动开发环境_第1张图片
至此,就完成了一个测试环境的搭建,由于 Bazel 原生的支持,不需要写类似下面的测试入口方法来运行测试:

#include "gtest/gtest.h"

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

接下来,直接使用 bazel run src:test_add,还可以使用 bazel test src:test_add 直接获取测试结果。

bazel run :test_add 
INFO: Analyzed target //src:test_add (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:test_add up-to-date:
  bazel-bin/src/test_add
INFO: Elapsed time: 0.151s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //src:test_add
-----------------------------------------------------------------------------
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from testAdd
[ RUN      ] testAdd.test0
[       OK ] testAdd.test0 (0 ms)
[----------] 1 test from testAdd (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 1 test.

小结

Bazel 最大的优势还是多语言构建,当然 Bazel 的功能不止这些,还有类似包可见性等概念并没有涉及。不过,还是可以看到 Bazel 的使用还是比较简单的(如果原生支持)。

你可能感兴趣的:(C/C++,Bazel,test)