使用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 = ["http://visibility:public"],
)

test/BUILD

test包下,描述測試用例的構建過程。

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

quantity/BUILD

quantity包下,描述目標quantity的構建過程。

cc_library(
  name = "quantity",
  srcs = glob(["**/*.cpp", "**/*.cc"]),
  hdrs = glob(["**/*.h", "**/*.hpp"]),
  visibility = ["http://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閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容