iOS TDD&BDD

TDD

Test Drive Development

  • 有利于更加專注軟件設(shè)計(jì)歇竟;
  • 清晰地了解軟件的需求;
  • 很好的詮釋了代碼即文檔敏储。
TDD

TDD是一種相對(duì)于普通思維的方式來說觉鼻,比較極端的一種做法。我們一般能想到的是先編寫業(yè)務(wù)代碼棋凳,然后為其編寫測(cè)試代碼拦坠,用來驗(yàn)證產(chǎn)品方法是不是按照設(shè)計(jì)工作。而TDD的思想正好與之相反贫橙,在TDD的世界中贪婉,我們應(yīng)該首先根據(jù)需求或者接口情況編寫測(cè)試,然后再根據(jù)測(cè)試來編寫業(yè)務(wù)代碼卢肃,而這其實(shí)是違反傳統(tǒng)軟件開發(fā)中的先驗(yàn)認(rèn)知的.

我們可以舉一個(gè)生活中類似的例子來說明TDD的必要性:有經(jīng)驗(yàn)的砌磚師傅總是會(huì)先拉一條垂線疲迂,然后沿著線砌磚,因?yàn)橛兄本€的保證莫湘,因此可以做到筆直整齊尤蒿;而新入行的師傅往往二話不說直接開工,然后在一階段完成后再用直尺垂線之類的工具進(jìn)行測(cè)量和修補(bǔ)幅垮。

TDD的好處不言自明腰池,因?yàn)榭偸窍葴y(cè)試,再編碼,所以至少你的所有代碼的public部分都應(yīng)該含有必要的測(cè)試示弓。另外讳侨,因?yàn)闇y(cè)試代碼實(shí)際是要使用產(chǎn)品代碼的,因此在編寫產(chǎn)品代碼前你將有一次深入思考和實(shí)踐如何使用這些代碼的機(jī)會(huì)奏属,這對(duì)提高設(shè)計(jì)和可擴(kuò)展性有很好的幫助跨跨,試想一下你測(cè)試都很難寫的接口,別人(或者自己)用起來得多糾結(jié)囱皿。在測(cè)試的準(zhǔn)繩下勇婴,你可以有目的有方向地編碼;另外嘱腥,因?yàn)橛袦y(cè)試的保護(hù)耕渴,你可以放心對(duì)原有代碼進(jìn)行重構(gòu),而不必?fù)?dān)心破壞邏輯齿兔。這些其實(shí)都指向了一個(gè)最終的目的:讓我們快樂安心高效地工作橱脸。

BDD

Behavior Drive Development

DBB

XCTest(作者注:蘋果官方測(cè)試框架)是基于OCUnit的傳統(tǒng)測(cè)試框架,在書寫性和可讀性上都不太好愧驱。在測(cè)試用例太多的時(shí)候慰技,由于各個(gè)測(cè)試方法是割裂的,想在某個(gè)很長(zhǎng)的測(cè)試文件中找到特定的某個(gè)測(cè)試并搞明白這個(gè)測(cè)試是在做什么并不是很容易的事情组砚。所有的測(cè)試都是由斷言完成的,而很多時(shí)候斷言的意義并不是特別的明確掏颊,對(duì)于項(xiàng)目交付或者新的開發(fā)人員加入時(shí)糟红,往往要花上很大成本來進(jìn)行理解或者轉(zhuǎn)換。另外乌叶,每一個(gè)測(cè)試的描述都被寫在斷言之后盆偿,夾雜在代碼之中,難以尋找准浴。使用XCTest測(cè)試另外一個(gè)問題是難以進(jìn)行mock或者stub事扭,而這在測(cè)試中是非常重要的一部分。

行為驅(qū)動(dòng)開發(fā)(BDD)正是為了解決上述問題而生的乐横,作為第二代敏捷方法求橄,BDD提倡的是通過將測(cè)試語句轉(zhuǎn)換為類似自然語言的描述,開發(fā)人員可以使用更符合大眾語言的習(xí)慣來書寫測(cè)試葡公,這樣不論在項(xiàng)目交接/交付罐农,或者之后自己修改時(shí),都可以順利很多

如果說作為開發(fā)者的我們?nèi)粘9ぷ魇菍懘a催什,那么BDD其實(shí)就是在講故事涵亏。一個(gè)典型的BDD的測(cè)試用例包活完整的三段式上下文,測(cè)試大多可以翻譯為Given..When..Then的格式,讀起來輕松愜意气筋。BDD在其他語言中也已經(jīng)有一些框架拆内,包括最早的Java的JBehave和赫赫有名的Ruby的RSpec和Cucumber。而在objc社區(qū)中BDD框架也正在欣欣向榮地發(fā)展宠默,得益于objc的語法本來就非常接近自然語言麸恍,再加上C語言宏的威力,我們是有可能寫出漂亮優(yōu)美的測(cè)試的光稼。在objc中或南,現(xiàn)在比較流行的BDD框架有cedar,specta和Kiwi艾君。

Quick + Nimble In Swift

https://github.com/Quick/Quick

// Swift

import Quick
import Nimble

class TableOfContentsSpec: QuickSpec {
override func spec() {
    describe("the 'Documentation' directory") {
  it("has everything you need to get started") {
    let sections = Directory("Documentation").sections
    expect(sections).to(contain("Organized Tests with Quick     Examples and Example Groups"))
    expect(sections).to(contain("Installing Quick"))
  }

  context("if it doesn't have what you're looking for") {
    it("needs to be updated") {
      let you = You(awesome: true)
      expect{you.submittedAnIssue}.toEventually(beTruthy())
    }
    }
}
}
}

Nimble

https://github.com/Quick/Nimble

!Swift
expect(1 + 1).to(equal(2))
expect(1.2).to(beCloseTo(1.1, within: 0.1))
expect(3) > 2
expect("seahorse").to(contain("sea"))
expect(["Atlantic", "Pacific"]).toNot(contain("Mississippi"))
expect(ocean.isClean).toEventually(beTruthy())

Specta

https://github.com/specta/specta

A light-weight TDD / BDD framework for Objective-C.

FEATURES

  • An Objective-C RSpec-like BDD DSL
  • Quick and easy set up
  • Built on top of XCTest
  • Excellent Xcode integration

Specta BDD DSL

  1. SpecBegin 聲明了一個(gè)測(cè)試類采够,SpecEnd 結(jié)束了類聲明
  2. describe (context) 塊聲明了一組實(shí)例
  3. it (example/specify) 是一個(gè)單一的例子
  4. beforeAll 是一個(gè)運(yùn)行于所有同級(jí)塊之前的塊,只運(yùn)行一次冰垄。afterAll 與beforeAll相反蹬癌,是在所有同級(jí)塊之后運(yùn)行的塊,只運(yùn)行一次虹茶。
  5. beforeEach/afterEach逝薪,在每個(gè)同級(jí)塊運(yùn)行的時(shí)候,都會(huì)運(yùn)行一次蝴罪,而beforeAll/afterAll只會(huì)運(yùn)行一次
  6. it/waitUntil/done()董济,異步調(diào)用,注意完成異步操作之后要门,必須調(diào)用done()函數(shù)虏肾,如下:
    !Objc
     it(@"should do some stuff asynchronously", ^{
    waitUntil(^(DoneCallback done) {
    // Async example blocks need to invoke done()     callback.
    done();
    });
     });

Expecta

https://github.com/specta/expecta

A matcher framework for Objective-C and Cocoa.

!Objc
waitUntil(^(DoneCallback done) {
        //query
        [manager getSedentaryDataModels:^(NSArray < DMSedentaryDataModel * > *modelList) {
            tempModel = modelList.firstObject;
            expect(modelList.count).to.equal(1);
            done();
        }];
    });
    expect(tempModel).notTo.beNil();
    expect(tempModel.prKey).notTo.equal(0);

OCMock

http://ocmock.org/reference/#creating-mock-objects

  • Creating mock objects
  • Stubbing methods
  • Verifying interactions
      !Objc
      __block id mockConnection = nil;

    beforeAll(^{
    
    });

    afterAll(^{
    
    });

    beforeEach(^{
    mockConnection = OCMClassMock([TwitterConnection class]);
    });

    afterEach(^{
    [mockConnection stopMocking];
    });

    it(@"is should be success", ^{
    
    TwitterViewController *controller = [[TwitterViewController alloc] init];
    controller.connection = mockConnection;
    
    //模擬fetchTweets方法返回預(yù)設(shè)值
    Tweet *testTweet = [[Tweet alloc] init];
    testTweet.userName = @"齊滇大圣";
    Tweet *testTweet2 = [[Tweet alloc] init];
    testTweet2.userName = @"美猴王";
    NSArray *tweetArray = @[testTweet,testTweet2];
    OCMStub([mockConnection fetchTweets]).andReturn(tweetArray);
    
    //模擬出來一個(gè)view類
    id mockView = OCMClassMock([TweetView class]);
    controller.tweetView = mockView;
    
    //這里執(zhí)行updateTweetView之后,[mockView addTweet:]加入了testTweet和testTweet2
    [controller updateTweetView];
    
    OCMVerify([mockView addTweet:testTweet]);
    OCMVerify([mockView addTweet:testTweet2]);
    OCMVerify([mockView addTweet:[OCMArg any]]);

Slather

https://github.com/SlatherOrg/slather

!Objc
source ~/.bash_profile
cd ${SRCROOT}
rm -rf  ${SRCROOT}/slather-html
slather coverage --html --output-directory  ${SRCROOT}/slather-html --scheme DeviceManager --workspace ${SRCROOT}/DeviceManager.xcworkspace ${SRCROOT}/DeviceManager.xcodeproj
open 'slather-html/index.html'
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欢搜,一起剝皮案震驚了整個(gè)濱河市封豪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炒瘟,老刑警劉巖吹埠,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疮装,居然都是意外死亡缘琅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門斩个,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胯杭,“玉大人,你說我怎么就攤上這事受啥∽龈觯” “怎么了鸽心?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)居暖。 經(jīng)常有香客問我顽频,道長(zhǎng),這世上最難降的妖魔是什么太闺? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任糯景,我火速辦了婚禮,結(jié)果婚禮上省骂,老公的妹妹穿的比我還像新娘蟀淮。我一直安慰自己,他們只是感情好钞澳,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布怠惶。 她就那樣靜靜地躺著,像睡著了一般轧粟。 火紅的嫁衣襯著肌膚如雪策治。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天兰吟,我揣著相機(jī)與錄音通惫,去河邊找鬼。 笑死混蔼,一個(gè)胖子當(dāng)著我的面吹牛履腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惭嚣,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼府树,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了料按?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤卓箫,失蹤者是張志新(化名)和其女友劉穎载矿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烹卒,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闷盔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旅急。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逢勾。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖藐吮,靈堂內(nèi)的尸體忽然破棺而出溺拱,到底是詐尸還是另有隱情逃贝,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布迫摔,位于F島的核電站沐扳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏句占。R本人自食惡果不足惜沪摄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纱烘。 院中可真熱鬧杨拐,春花似錦、人聲如沸擂啥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啤它。三九已至奕筐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間变骡,已是汗流浹背离赫。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塌碌,地道東北人渊胸。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像台妆,于是被迫代替她去往敵國(guó)和親翎猛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容