使用Cut快速构建C++程序的TDD环境

本文通过Bazel(Google Build Tool)构建工具,使用Cut(C++ Unified Test Framework)快速构建一个C++程序的TDD环境,以此阐述Cut在实际项目中的实际应用。

一般地,一个C++程序为了实施TDD,必须先安装测试框架(例如Cut)到系统目录(一般地,默认为/usr/local),但这样可能造成复杂的版本管理问题。

这个案例实践中,只需要安装Bazel构建工具,无需事先安装Cut的依赖,便可以开始TDD之旅了。因为Bazel为工程建立独立的构建环境,并且按照依赖规则,会自动下载,并构建外部依赖,例如Cut测试框架。

其中,Bazel是典型的声明式构建工具,相对于Make工具,具有良好的用户友好性。但其执行过程必然是命令式的,其背后的本质必然与Make的过程相似。

安装Bazel

参考Bazel的官方网站:https://bazel.build,查阅Bazel安装相关安装手册,在此不再冗述。

需求

通过一个简单的例子,讲解一个典型的C++程序如何使用Cut进行TDD开发。需求非常简单,存在两个单位计量体系,实现一个单位数值比较的程序库。

- Length:
  1 Mile == 1760 Yard
  1 Yard == 3 Feet
  1 Feet == 12 Inch
    
- Volume:
  1 TBSP == 3 TSP
  1 OZ   == 2 TBSP

工程构建

Quantity使用Bazel构建,其物理结构如下图所示。

quantity
├── WORKSPACE
├── cut.BUILD
├── quantity
│   ├── BUILD
│   ├── base
│   │   ├── Amount.h
│   │   ├── Quantity.h
│   │   └── Quantity.hpp
│   ├── length
│   │   ├── Length.cpp
│   │   └── Length.h
│   └── volume
│       ├── Volume.cpp
│       └── Volume.h
└── test
    ├── BUILD
    ├── LengthTest.cpp
    ├── VolumeTest.cpp
    └── main.cpp

WORKSPACE

使用WORKSPACE,声明项目quantity依赖于cut,它在执行测试时,会自动去Github下载源代码,并执行编译。

new_http_archive(
  name = "cut",
  url = "https://github.com/horance-liu/cut/archive/release-1.0.0.tar.gz",
  build_file = "cut.BUILD",
  strip_prefix = "cut-release-1.0.0",
)

cut.BUILD

描述Cut的构建过程。

cc_library(
  name = "cut",
  srcs = glob(["src/**/*.cpp"]),
  hdrs = glob(["include/**/*.h"]),
  copts = ["-Iexternal/cut/include"],
  visibility = ["//visibility:public"],
)

test/BUILD

test包下,描述测试用例的构建过程。

cc_test(
  name = "quantity-test",
  srcs = glob(["**/*.cpp", "**/*.cc"]),
  copts = ["-Iexternal/cut/include"],
  deps = [ "@cut//:cut",
    "//quantity:quantity" ],
)

quantity/BUILD

quantity包下,描述目标quantity的构建过程。

cc_library(
  name = "quantity",
  srcs = glob(["**/*.cpp", "**/*.cc"]),
  hdrs = glob(["**/*.h", "**/*.hpp"]),
  visibility = ["//visibility:public"],
)

实现

Base组件

quantity/base/Amount.h

#ifndef H21E7D6D3_9F51_40E8_957C_72D0DBF81D69
#define H21E7D6D3_9F51_40E8_957C_72D0DBF81D69

using Amount = unsigned int;

#endif

quantity/base/Quantity.h

#ifndef HE781FE8C_8C1B_490C_893C_B3412F6CB478
#define HE781FE8C_8C1B_490C_893C_B3412F6CB478

#include "quantity/base/Amount.h"

template <typename Unit>
struct Quantity
{
    Quantity(Amount amount, Unit unit);

    bool operator==(const Quantity&) const;
    bool operator!=(const Quantity&) const;

private:
    const Amount amountInBaseUnit;
};

#endif

quantity/base/Quantity.hpp

#include "quantity/base/Quantity.h"

template <typename Unit>
Quantity<Unit>::Quantity(Amount amount, Unit unit)
  : amountInBaseUnit(unit * amount)
{
}

template <typename Unit>
bool Quantity<Unit>::operator==(const Quantity& rhs) const
{
    return amountInBaseUnit == rhs.amountInBaseUnit;
}

template <typename Unit>
bool Quantity<Unit>::operator!=(const Quantity& rhs) const
{
    return !(*this == rhs);
}

Length组件

length/Length.h

#ifndef HF21A561D_09DF_4686_935D_4B7CD6FD9A2B
#define HF21A561D_09DF_4686_935D_4B7CD6FD9A2B

enum LengthUnit
{
    INCH = 1,
    FEET = 12 * INCH,
    YARD = 3 * FEET,
    MILE = 1760 * YARD,
};

using Length = Quantity<LengthUnit>;

#endif

length/Length.cpp

#include "quantity/length/Length.h"
#include "quantity/base/Quantity.hpp"

template struct Quantity<LengthUnit>;

Volume组件

volume/Volume.h

#ifndef HA0A7C92D_2A2A_47D0_B89D_ED2AFF645F23
#define HA0A7C92D_2A2A_47D0_B89D_ED2AFF645F23

#include "quantity/base/Quantity.h"

enum VolumeUnit
{
    TSP  = 1,
    TBSP = 3 * TSP,
    OZ   = 2 * TBSP,
};

using Volume = Quantity<VolumeUnit>;

#endif

volume/Volume.cpp

#include "quantity/volume/Volume.h"
#include "quantity/base/Quantity.hpp"

template struct Quantity<VolumeUnit>;

测试用例

test/LengthTest.cpp

#include "cut/cut.hpp"
#include "quantity/length/Length.h"

USING_CUM_NS

FIXTURE(LengthTest)
{
    TEST("1 feet should equal to 12 inch")
    {
        ASSERT_THAT(Length(1, FEET), eq(Length(12, INCH)));
    }

    TEST("1 yard should equal to 3 feets")
    {
        ASSERT_THAT(Length(1, YARD), eq(Length(3, FEET)));
    }

    TEST("1 mile should equal to 1760 yards")
    {
        ASSERT_THAT(Length(1, MILE), eq(Length(1760, YARD)));
    }
};

volume/VolumeTest.cpp

#include "cut/cut.hpp"
#include "quantity/volume/Volume.h"

USING_CUM_NS

FIXTURE(VolumeTest)
{
    TEST("1 tbsp should equal to 3 tsps")
    {
        ASSERT_THAT(Volume(1, TBSP), eq(Volume(3, TSP)));
    }

    TEST("1 oz should equal to 2 tbsps")
    {
        ASSERT_THAT(Volume(1, OZ), eq(Volume(2, TBSP)));
    }
};

test/main.cpp

#include "cut/cut.hpp"

int main(int argc, char** argv)
{
    return cut::run_all_tests(argc, argv);
}

运行测试

$ bazel test //quantity:quantity-test

Github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容