聊聊iOS&OSX測(cè)試相關(guān)的八卦

說(shuō)真的务荆,自己在做客戶(hù)端開(kāi)發(fā)的時(shí)候基本沒(méi)有寫(xiě)過(guò)單元測(cè)試。????穷遂,不是對(duì)自己技術(shù)有多自信函匕,而是因?yàn)榛久Σ贿^(guò)來(lái),加上上頭沒(méi)明確要求蚪黑,就得過(guò)且過(guò)了盅惜。不過(guò)作為一個(gè)比較合格的攻城獅中剩,對(duì)單元測(cè)試還是需要了解的。今天總結(jié)一下在開(kāi)發(fā)iOS&OSX中從蘋(píng)果自家的XCTest到牛的一比的第三方開(kāi)源測(cè)試框架抒寂。由于東西多结啼,一時(shí)半會(huì)弄不完,只做簡(jiǎn)單入門(mén)介紹屈芜。后續(xù)會(huì)一一介紹郊愧。

從Xcode談起

蘋(píng)果在Xcode5中推出了一個(gè)簡(jiǎn)單而有效的框架--XCTest用于做單元測(cè)試。XCTest是以xUnit way的方式寫(xiě)的井佑。

寫(xiě)XCTest測(cè)試用例非常簡(jiǎn)單属铁,而且開(kāi)發(fā)者通過(guò)IDE點(diǎn)擊?U可以非常迅速的獲取Xcode的反饋信息。同時(shí)Xcode也有一個(gè)專(zhuān)門(mén)用戶(hù)測(cè)試的區(qū)域毅糟,如下圖红选,在這里我們可以看到所有的測(cè)試用例成功或者失敗的情況。

通過(guò)的測(cè)試用例用綠色標(biāo)識(shí)姆另,未通過(guò)的測(cè)試用例用紅色標(biāo)識(shí)喇肋。

雖然XCTestXcode集成在一起非常方便,非常容易使用迹辐,但是也有一些問(wèn)題蝶防,比如XCTAssert...API不是那么的有表現(xiàn)力和通用。尤其拋開(kāi)了XcodeXCTest會(huì)比較麻煩明吩。

這里有篇介紹的不錯(cuò)的入門(mén)介紹:

有精力的還是推薦看

隨著技術(shù)發(fā)展间学,為了更為方便的寫(xiě)UI測(cè)試。蘋(píng)果t提供了UI測(cè)試框架--UIAutomation印荔。UIAutomation通過(guò)JS來(lái)寫(xiě)測(cè)試低葫,并且允許開(kāi)發(fā)者驅(qū)動(dòng)應(yīng)用程序的UI并且在不同的狀態(tài)插入斷言。盡管這聽(tīng)說(shuō)起來(lái)有點(diǎn)牛逼仍律,但是真真的通過(guò)UIAutomation來(lái)寫(xiě)測(cè)試用例嘿悬,結(jié)果會(huì)變得非常糟糕。并且JS的API沒(méi)有原生的單元測(cè)試API強(qiáng)大水泉。

來(lái)看看JS寫(xiě)的:

UIATarget.localTarget().frontMostApp().navigationBar().buttons()["Add"].tap();

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()[0].elements()["Chocolate Cake"];

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].scrollToElementWithPredicate("name beginswith 'Turtle Pie'");

正如你所見(jiàn)善涨,麻蛋居然比我寫(xiě)OC的代碼還冗長(zhǎng)。并且必須使用Instruments來(lái)運(yùn)行草则「峙。總的來(lái)說(shuō)就是不爽。蘋(píng)果推出的最新的方案是Xcode Bot,舉個(gè)例子炕横,可以通過(guò)安裝Xcode Server來(lái)自動(dòng)跑測(cè)試用例源内。更加具體的內(nèi)容可以看看看官方文檔。

以上是蘋(píng)果自家的方案份殿。

測(cè)試相關(guān)的開(kāi)源庫(kù)

關(guān)于iOS和OSX的開(kāi)源項(xiàng)目還是有很多的姿锭。就單單來(lái)講塔鳍,收錄到Cocoapods的項(xiàng)目而言。通過(guò)Pod list就可以知道有多少個(gè)了呻此,我本機(jī)試了一下,一共有24173 pods were found腔寡。在這么多的第三方中焚鲜,有一些非常不錯(cuò)的開(kāi)發(fā)測(cè)試框架。

單元測(cè)試的開(kāi)源庫(kù)大致都遵循了一種測(cè)試風(fēng)格放前,事實(shí)上就是xSpec style,這種風(fēng)格來(lái)至于Ruby測(cè)試庫(kù)RSpec.提供了測(cè)試類(lèi)更多的操作忿磅,而不僅僅只是枚舉方法。

Kiwi

kiwi是一個(gè)行為驅(qū)動(dòng)開(kāi)發(fā)庫(kù)凭语,可以完全替代XCTest,語(yǔ)言用得是OC,所以開(kāi)發(fā)上手也比較快葱她。來(lái)看看具體怎么寫(xiě):

describe(@"Team", ^{
  context(@"when newly created", ^{
    it(@"has a name", ^{
      id team = [Team team];
      [[team.name should] equal:@"Black Hawks"];
    });

    it(@"has 11 players", ^{
      id team = [Team team];
      [[[team should] have:11] players];
    });
  });
});

Kiwi規(guī)范更容易閱讀和溝通。而且在它的wiki里面寫(xiě)的非常詳細(xì)似扔。并且有自己的一套規(guī)范吨些。有如下內(nèi)容:

  • Specs:
  • Expectations:
  • Mocks and Stubs:
  • Asynchronous Testing:

具體請(qǐng)看kiwi-bdd/Kiwi

Specta

specta是一個(gè)輕量級(jí)的TDD/BDD的測(cè)試框架,同樣適用OC語(yǔ)言編寫(xiě)炒辉。相對(duì)于kiwi而言豪墅,kiwi更為龐大,specta更為輕量級(jí)黔寇、組件化偶器。

示例代碼如下:

SpecBegin(Thing)

describe(@"Thing", ^{
  it(@"should do stuff", ^{
    // This is an example block. Place your assertions here.
  });

  it(@"should do some stuff asynchronously", ^{
    waitUntil(^(DoneCallback done) {
      // Async example blocks need to invoke done() callback.
      done();
    });
  });
});

specta有一些依賴(lài)的工具,可以根據(jù)需要添加缝裤。比如:

target :MyApp do
# your app dependencies

  target :MyAppTests do
    inherit! :search_paths

    pod 'Specta', '~> 1.0'
    # pod 'Expecta',     '~> 1.0'   # expecta matchers
    # pod 'OCMock',      '~> 2.2'   # OCMock
    # pod 'OCHamcrest',  '~> 3.0'   # hamcrest matchers
    # pod 'OCMockito',   '~> 1.0'   # OCMock
    # pod 'LRMocky',     '~> 0.9'   # LRMocky
  end
end

Quick

quick相對(duì)而言比較年輕屏轰,適用Swift語(yǔ)言編寫(xiě)的,但是目前star數(shù)卻高于前面介紹的框架憋飞。隨便說(shuō)一下霎苗,quick依賴(lài)一個(gè)匹配框架Nimble,彌補(bǔ)了XCTAssert()使用起來(lái)不夠明確搀崭,清晰的缺點(diǎn)叨粘。

感受一下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())

是不是比XCTAssert()用起來(lái)舒服一些。

得益于swift語(yǔ)法及閉包瘤睹,相對(duì)于之前介紹的框架升敲,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())
        }
      }
    }
  }
}

用于UI測(cè)試的開(kāi)源庫(kù)

相對(duì)于XCTest而言轰传,由于蘋(píng)果排斥第三方工具關(guān)于UI測(cè)試方面只有UIAutomation驴党。剩下的方案只有hack了。

KIF

kif展開(kāi)其實(shí)就是keep it functional,用OC寫(xiě)获茬,基于XCTexst港庄。
KIF的tester使用私有API訪問(wèn)視圖層次倔既,并通過(guò)視圖的accessiblity label value進(jìn)行交互。

示例代碼如下:

- (void)testSuccessfulLogin {
  [tester enterText:@"user@example.com" intoViewWithAccessibilityLabel:@"Login User Name"];
  [tester enterText:@"thisismypassword" intoViewWithAccessibilityLabel:@"Login Password"];
  [tester tapViewWithAccessibilityLabel:@"Log In"];

  // Verify that the login succeeded
  [tester waitForTappableViewWithAccessibilityLabel:@"Welcome"];
}

注意:KIF需要被加入到項(xiàng)目中的Unit Test target而不是UI Test target鹏氧。

總的來(lái)說(shuō)是目前比較優(yōu)秀的iOS開(kāi)源庫(kù)中UI 測(cè)試框架渤涌。
這里有一篇美團(tuán)分享的關(guān)于它的使用。詳細(xì)介紹請(qǐng)看基于 KIF 的 iOS UI 自動(dòng)化測(cè)試和持續(xù)集成

Subliminal

sublimnial是一個(gè)OC框架把还,和KIF對(duì)比而言实蓬,二者都是集成了XCTest,不同之處是sublimnial實(shí)在UIAtomation基礎(chǔ)上實(shí)現(xiàn)的,而KIF是利用 蘋(píng)果 給所有控件提供的輔助屬性 accessibility attributes來(lái)定位和獲取元素吊履,完成界面的交互操作安皱;結(jié)合使用 Xcode 的 XCTest 測(cè)試框架,擁有 XCTest 測(cè)試框架的特性艇炎,使得測(cè)試用例能以 command line build 工具運(yùn)行并獲取測(cè)試報(bào)告酌伊。

示例代碼:

- (void)testLogInSucceedsWithUsernameAndPassword {
  SLTextField *usernameField = [SLTextField elementWithAccessibilityLabel:@"username field"];
  SLTextField *passwordField = [SLTextField elementWithAccessibilityLabel:@"password field" isSecure:YES];
  SLElement *submitButton = [SLElement elementWithAccessibilityLabel:@"Submit"];
  SLElement *loginSpinner = [SLElement elementWithAccessibilityLabel:@"Logging in..."];

  NSString *username = @"Jeff", *password = @"foo";
  [usernameField setText:username];
  [passwordField setText:password];

  [submitButton tap];

  // wait for the login spinner to disappear
  SLAssertTrueWithTimeout([loginSpinner isInvalidOrInvisible], 3.0, @"Log-in was not successful.");

  NSString *successMessage = [NSString stringWithFormat:@"Hello, %@!", username];
  SLAssertTrue([[SLElement elementWithAccessibilityLabel:successMessage] isValid],
  @"Log-in did not succeed.");

  // Check the internal state of the app.
  SLAssertTrue(SLAskAppYesNo(isUserLoggedIn), @"User is not logged in.")
}

不幸的是這個(gè)庫(kù)已經(jīng)很久很久沒(méi)有維護(hù)了。

Calabash

上面介紹的框架都是針對(duì)于iOS或者OSX缀踪,Calabash是一個(gè)跨平臺(tái)的測(cè)試框架居砖。Xamarin維護(hù)這種鴿項(xiàng)目,可能有些同學(xué)對(duì)Xamarin不是很熟悉辜贵,這個(gè)公司主要使用C#來(lái)寫(xiě)iOS和Android應(yīng)用程序悯蝉。

不像KIF和Subliminal不用和Xcode集成,但是寫(xiě)方法就和OC有比較大的區(qū)別托慨。因?yàn)?code>Calabash是基于Ruby的鼻由。

示例代碼:

# rating_a_stand.feature

Feature: Rating a stand
  Scenario: Find and rate a stand from the list
    Given I am on the foodstand list
    Then I should see a "rating" button
    And I should not see "Dixie Burger & Gumbo Soup"

# steps.rb

Given(/^I am on the foodstand list$/) do
  wait_for_element_exists "view marked:'Foodstand'"
end

Given(/^I should see a "([^\"]*)" button$/) do |button_title|
  wait_for_element_exists "button marked:'#{button_title}'"
end

Given(/^I should not see "([^\"]*)"$/) do |view_label|
  wait_for_element_does_not_exists "view marked:'#{view_label}'
end

目前本人還沒(méi)有用過(guò)這個(gè)牛逼的測(cè)試框架。如果有人感興趣可以嘗試一下厚棵。Calabash官網(wǎng)

聊聊其他

上面粗略的介紹了iOS或者OSX官方自帶及開(kāi)源的測(cè)試框架蕉世。只是起到了入門(mén)簡(jiǎn)單介紹的作用。讀者完全可以自己實(shí)踐體驗(yàn)下一下各個(gè)框架的優(yōu)劣婆硬。

聊了下測(cè)試相關(guān)的狠轻,如果再繼續(xù)深入。就是持續(xù)集成彬犯,持續(xù)部署這些了向楼。這塊網(wǎng)上也有不少文章介紹的。

Have Fun!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谐区,一起剝皮案震驚了整個(gè)濱河市湖蜕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宋列,老刑警劉巖昭抒,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡灭返,警方通過(guò)查閱死者的電腦和手機(jī)盗迟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)熙含,“玉大人罚缕,你說(shuō)我怎么就攤上這事≡蹙玻” “怎么了怕磨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)消约。 經(jīng)常有香客問(wèn)我,道長(zhǎng)员帮,這世上最難降的妖魔是什么或粮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮捞高,結(jié)果婚禮上氯材,老公的妹妹穿的比我還像新娘。我一直安慰自己硝岗,他們只是感情好氢哮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著型檀,像睡著了一般冗尤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胀溺,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天裂七,我揣著相機(jī)與錄音,去河邊找鬼仓坞。 笑死背零,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的无埃。 我是一名探鬼主播徙瓶,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嫉称!你這毒婦竟也來(lái)了侦镇?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤澎埠,失蹤者是張志新(化名)和其女友劉穎虽缕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氮趋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年伍派,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剩胁。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诉植,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昵观,到底是詐尸還是另有隱情晾腔,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布啊犬,位于F島的核電站灼擂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏觉至。R本人自食惡果不足惜剔应,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望语御。 院中可真熱鬧峻贮,春花似錦、人聲如沸应闯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)碉纺。三九已至船万,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惜辑,已是汗流浹背唬涧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盛撑,地道東北人碎节。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像抵卫,于是被迫代替她去往敵國(guó)和親狮荔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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

  • 大多數(shù)的iOS App (沒(méi)有持續(xù)集成)迭代流程是這樣的: 也就是說(shuō)介粘,測(cè)試是發(fā)布之前的最后一道關(guān)卡殖氏。如果bug不能...
    伯牙呀閱讀 4,893評(píng)論 1 22
  • 前言 如果有測(cè)試大佬發(fā)現(xiàn)內(nèi)容不對(duì),歡迎指正姻采,我會(huì)及時(shí)修改雅采。 大多數(shù)的iOS App(沒(méi)有持續(xù)集成)迭代流程是這樣的...
    默默_David閱讀 1,673評(píng)論 0 4
  • 1. XCTest XCTest是蘋(píng)果在iOS 7和Xcode5引入的一個(gè)簡(jiǎn)單而強(qiáng)大的測(cè)試框架,它的測(cè)試編寫(xiě)起來(lái)非...
    櫻落e_e閱讀 23,967評(píng)論 5 36
  • 前一陣子偶遇高中同學(xué)小王,和他談起近況婚瓜,他滿(mǎn)是抱怨宝鼓,工作不如意,對(duì)象看他不起于是對(duì)他越來(lái)越冷淡巴刻。他用游戲來(lái)麻痹...
    空城墨城閱讀 364評(píng)論 0 0
  • 心靈和誠(chéng)實(shí)的敬拜 耶穌說(shuō):“神是個(gè)靈愚铡,(或無(wú)個(gè)字)所以拜祂的,必須用心靈和誠(chéng)實(shí)拜祂胡陪×ち龋”(約翰福音4:24) 引言 ...
    破碎自我閱讀 8,276評(píng)論 0 0