国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Kiwi,BDD行為測試框架--iOS攻城獅進階必備技能

2019-11-14 17:53:09
字體:
供稿:網(wǎng)友

簡介

Kiwi 是一個適用于iOS開發(fā)的行為驅(qū)動測試框架,旨在提供一個足夠簡單易用的BDD庫.

使用Cocopods 安裝

target :AmazingAPPTests, :exclusive => true do  pod 'Kiwi'end

把 AmazingAppTests 改為你自己的工程中的Tests target的名字,比如我的是 iOS122Tests,然后更新即可:

pod update --verbose --no-repo-update

為了快速測試Kiwi是否安裝成功,你可以用下面的代碼替換到你的 Tests目錄下已有的文件中的默認內(nèi)容,然后點擊Xcode導(dǎo)航欄 PRoduct->Test(或者使用快捷鍵 cmd + u),此時如果提示你 Test Failed,點擊錯誤提示,會在左側(cè)第四導(dǎo)航欄看到類似下面的錯誤:

Assertions: 'Math, is pretty cool' [FAILED], expected subject to equal (KWValue) 43, got (KWValue) 42File: MathSpec.m:9

如果不能看到上述錯誤信息,說明你的工程配置可能有問題,可以參考這里詳細微調(diào)下: Getting Started with Kiwi 2.0

規(guī)則

Kiwi的規(guī)則由以下元素組成

  • #import "Kiwi.h" 導(dǎo)入Kiwi庫.這應(yīng)該在規(guī)則的文件開始處最先導(dǎo)入.
  • SPEC_BEGIN(ClassName)SPEC_END 宏,用于標(biāo)記 KWSpec 類的開始和結(jié)束,以及測試用例的分組聲明.
  • registerMatchers(aNamespacePrefix) 注冊所有使用指定命名空間前綴的匹配器.除了Kiwi默認的匹配器,這些匹配器也可以在當(dāng)前規(guī)則中使用.
  • describe(aString, aBlock) 開啟一個上下文環(huán)境,可包含測試用例或嵌套其他的上下文環(huán)境.
  • 為了使一個block中使用的變量真正被改變,它需要在定義時使用 __block 修飾符.
  • beforeAll(aBlock) 在所有內(nèi)嵌上下文或當(dāng)前上下文的`itblock執(zhí)行之前執(zhí)行一次.
  • afterAll(aBlock) 在所有內(nèi)嵌上下文或當(dāng)前上下文的`itblock執(zhí)行之后執(zhí)行一次.
  • beforeEach(aBlock) 在所有包含的上下文環(huán)境的 itblock執(zhí)行之前,均各執(zhí)行一次.用于初始化指定上下文環(huán)境的代碼,應(yīng)該放在這里.
  • afterEach(aBlock) 在所有包含的上下文環(huán)境的 itblock執(zhí)行之后,均各執(zhí)行一次.
  • it(aString, aBlock) 聲明一個測試用例.這里描述了對對象或行為的期望.
  • specify(aBlock) 聲明一個沒有描述的測試用例.這個常用于簡單的期望.
  • pending(aString, aBlock) 可用于標(biāo)記尚未完成的功能或用例,僅會使Xcode輸出一個黃色警告.(有點TODO的趕腳)
  • let(subject, aBlock) 聲明一個本地工具變量,這個變量會在規(guī)則內(nèi)所有上下文的每個 itblock執(zhí)行前,重新初始化一次.

示例.

#import "Kiwi.h"#import "YFKiwiSample.h"SPEC_BEGIN(SpecName)describe(@"ClassName", ^{    registerMatchers(@"BG"); // 注冊 BGTangentMatcher, BGConvexMatcher 等.        context(@"a state the component is in", ^{        let(variable, ^{ // 在每個包含的 "it" 執(zhí)行前執(zhí)行執(zhí)行一次.            return [[YFKiwiSample alloc]init];        });                beforeAll(^{ // 執(zhí)行一次            NSLog(@"beforAll");        });                afterAll(^{ // Occurs once            NSLog(@"afterAll");        });                beforeEach(^{ // 在每個包含的 "it" 執(zhí)行前,都執(zhí)行一次.            NSLog(@"beforeEach");        });                afterEach(^{ // 在每個包含的 "it" 執(zhí)行后,都執(zhí)行一次.            NSLog(@"afterEach");        });                it(@"should do something", ^{            NSLog(@"should do something");//            [[variable should] meetSomeExpectation];        });                specify(^{            NSLog(@"specify");            [[variable shouldNot] beNil];        });                context(@"inner context", ^{            NSLog(@"inner context");            it(@"does another thing", ^{                NSLog(@"does another thing");            });                        pending(@"等待實現(xiàn)的東西", ^{                NSLog(@"等待實現(xiàn)的東西");            });        });    });});SPEC_END

期望

期望,用來驗證用例中的對象行為是否符合你的語氣.一個期望,具有如下形式: [[subject should] someCondition:anArgument].此處 [subject should]是表達式的類型, ... someCondition:anArgument] 是匹配器的表達式.

示例:

// 可以用下面的內(nèi)容替換原來的tests.m中的內(nèi)容,然后cmd+u// ;測試失敗可自行解決;解決不了的,繼續(xù)往下看.#import "Kiwi.h"#import "YFKiwiCar.h"SPEC_BEGIN(CarSpec)describe(@"YFKiwiCar", ^{    it(@"A Car Rule", ^{        id car = [YFKiwiCar new];        [[car shouldNot] beNil];        [[car should] beKindOfClass:[YFKiwiCar class]];        [[car shouldNot] conformToProtocol:@protocol(NSCopying)];        [[[car should] have:4] wheels];        [[theValue([(YFKiwiCar *)car speed]) should] equal:theValue(42.0f)];        [[car should] receive:@selector(changeToGear:) withArguments: theValue(3)];                 [car changeToGear: 3];    });});SPEC_END

should 和 shouldNot

[subject should][subject shouldNot] 表達式,類似于一個接收器,用于接收一個期望匹配器.他們后面緊跟的是真實的匹配表達式,這些表達式將真正被用于計算.

默認地,主語守衛(wèi)(一種機制,可以保證nil不引起崩潰)也會在[subject should ][subject shouldNot]被使用時創(chuàng)建.給 nil 發(fā)送消息,通常不會有任何副作用.但是,你幾乎不會希望:一個表達式,只是為了給某個對象傳遞一個無足輕重的消息,就因為對象本身是nil.也就說,向nil對象本身發(fā)送消息,并不會有任何副作用;但是在BBD里,某個要被傳遞消息的對象是nil,通常是非預(yù)期行為.所以,這些表達式的對象守衛(wèi)機制,會將左側(cè)無法判定為不為nil的表達式判定為 fail失敗.

標(biāo)量裝箱

"裝箱"是固定術(shù)語譯法,其實即使我們iOS常說的基本類型轉(zhuǎn)NSObject類型(事實如此,勿噴).

部分表達式中,匹配器表達式的參數(shù)總是NSObject對象.當(dāng)將一個標(biāo)量(如int整型,float浮點型等)用于需要id類型參數(shù)的地方時,應(yīng)使用theValue(一個標(biāo)量)宏將標(biāo)量裝箱.這種機制也適用于: 當(dāng)一個標(biāo)量需要是一個表達式的主語(主謂賓,基本語法規(guī)則,請自行腦補)時,或者一個 存根 的值需要是一個標(biāo)量時.

示例:

[[theValue(1 + 1) should] equal:theValue(2)];[[theValue(YES) shouldNot] equal:theValue(NO)];[[theValue(20u) should] beBetween:theValue(1) and:theValue(30.0)];    YFKiwiCar * car = [YFKiwiCar new];[[theValue(car.speed) should] beGreaterThan:theValue(40.0f)];

消息模式

在iOS中,常將調(diào)用某個實例對象的方法成為給這個對象發(fā)送了某個消息.所以"消息模式"中的"消息",更多的指的的實例對象的方法;"消息模式"也就被用來判斷對象的某個方法是否會調(diào)用以及是否會按照預(yù)期的方式調(diào)用.

一些 Kiwi 匹配器支持使用消息模式的期望.消息模式部分,常被放在一個表達式的后部,就像一個將要發(fā)給主語的消息一樣.

示例:

YFKiwiCar * cruiser = [[YFKiwiCar alloc]init];    [[cruiser should] receive:@selector(jumpToStarSystemWithIndex:) withArguments: theValue(3)];    [cruiser jumpToStarSystemWithIndex: 3];

期望:數(shù)值 和 數(shù)字

  • [[subject shouldNot] beNil]
  • [[subject should] beNil]
  • [[subject should] beIdenticalTo:(id)anObject] - 比較是否完全相同
  • [[subject should] equal:(id)anObject]
  • [[subject should] equal:(double)aValue withDelta:(double)aDelta]
  • [[subject should] beWithin:(id)aDistance of:(id)aValue]
  • [[subject should] beLessThan:(id)aValue]
  • [[subject should] beLessThanOrEqualTo:(id)aValue]
  • [[subject should] beGreaterThan:(id)aValue]
  • [[subject should] beGreaterThanOrEqualTo:(id)aValue]
  • [[subject should] beBetween:(id)aLowerEndpoint and:(id)anUpperEndpoint]
  • [[subject should] beInTheIntervalFrom:(id)aLowerEndpoint to:(id)anUpperEndpoint]
  • [[subject should] beTrue]
  • [[subject should] beFalse]
  • [[subject should] beYes]
  • [[subject should] beNo]
  • [[subject should] beZero]

期望: 子串匹配

  • [[subject should] containString:(NSString*)substring]
  • [[subject should] containString:(NSString*)substring options:(NSStringCompareOptions)options]
  • [[subject should] startWithString:(NSString*)prefix]
  • [[subject should] endWithString:(NSString*)suffix]

示例:

    [[@"Hello, world!" should] containString:@"world"];    [[@"Hello, world!" should] containString:@"WORLD" options:NSCaseInsensitiveSearch];    [[@"Hello, world!" should] startWithString:@"Hello,"];    [[@"Hello, world!" should] endWithString:@"world!"];

期望: 正則表達式匹配

  • [[subject should] matchPattern:(NSString*)pattern]
  • [[subject should] matchPattern:(NSString*)pattern options:(NSRegularExpressionOptions)options]
    [[@"ababab" should] matchPattern:@"(ab)+"];    [[@" foo " shouldNot] matchPattern:@"^foo$"];    [[@"abABab" should] matchPattern:@"(ab)+" options:NSRegularExpressionCaseInsensitive];

期望: 數(shù)量的變化

  • [[theBlock(^{ ... }) should] change:^{ return (NSInteger)count; }]
  • [[theBlock(^{ ... }) should] change:^{ return (NSInteger)count; } by:+1]
  • [[theBlock(^{ ... }) should] change:^{ return (NSInteger)count; } by:-1]

示例:

    it(@"Expectations: Count changes", ^{        NSMutableArray * array = [NSMutableArray arrayWithCapacity: 42];                [[theBlock(^{            [array addObject:@"foo"];        }) should] change:^{            return (NSInteger)[array count];        } by:+1];                [[theBlock(^{            [array addObject:@"bar"];            [array removeObject:@"foo"];        }) shouldNot] change:^{ return (NSInteger)[array count]; }];                [[theBlock(^{            [array removeObject:@"bar"];        }) should] change:^{ return (NSInteger)[array count]; } by:-1];    });

期望: 對象測試

  • [[subject should] beKindOfClass:(Class)aClass]
  • [[subject should] beMemberOfClass:(Class)aClass]
  • [[subject should] conformToProtocol:(Protocol *)aProtocol]
  • [[subject should] respondToSelector:(SEL)aSelector]

期望: 集合

對于集合主語(即,主語是集合類型的):

  • [[subject should] beEmpty]
  • [[subject should] contain:(id)anObject]
  • [[subject should] containObjectsInArray:(NSArray *)anArray]
  • [[subject should] containObjects:(id)firstObject, ...]
  • [[subject should] haveCountOf:(NSUInteger)aCount]
  • [[subject should] haveCountOfAtLeast:(NSUInteger)aCount]
  • [[subject should] haveCountOfAtMost:(NSUInteger)aCount]

對于集合鍵(即此屬性/方法名對應(yīng)/返回一個集合類型的對象):

  • [[[subject should] have:(NSUInteger)aCount] collectionKey]
  • [[[subject should] haveAtLeast:(NSUInteger)aCount] collectionKey]
  • [[[subject should] haveAtMost:(NSUInteger)aCount] collectionKey]

如果主語是一個集合(比如 NSArray數(shù)組), coollectionKey 可以是任何東西(比如 items),只要遵循語法結(jié)構(gòu)就行.否則, coollectionKey應(yīng)當(dāng)是一個可以發(fā)送給主語并返回集合類型數(shù)據(jù)的消息.

更進一步說: 對于集合類型的主語,coollectionKey的數(shù)量總是根據(jù)主語的集合內(nèi)的元素數(shù)量, coollectionKey 本身并無實際意義.

示例:

    NSArray *array = [NSArray arrayWithObject:@"foo"];    [[array should] have:1] item];        Car *car = [Car car];    [car setPassengers:[NSArray arrayWithObjects:@"Eric", "Stan", nil]];    [[[[car passengers] should] haveAtLeast:2] items];    [[[car should] haveAtLeast:2] passengers];

期望: 交互和消息

這些期望用于驗證主語是否在從創(chuàng)建期望到用例結(jié)束的這段時間里接收到了某個消息(或者說對象的某個方法是否被調(diào)用).這個期望會同時存儲 選擇器或參數(shù)等信息,并依次來決定期望是否滿足.

這些期望可用于真實或模擬的獨享,但是在設(shè)置 receive 表達式時,Xcode 可能會給警告(報黃).

對參數(shù)無要求的選擇器:

  • [[subject should] receive:(SEL)aSelector]
  • [[subject should] receive:(SEL)aSelector withCount:(NSUInteger)aCount]
  • [[subject should] receive:(SEL)aSelector withCountAtLeast:(NSUInteger)aCount]
  • [[subject should] receive:(SEL)aSelector withCountAtMost:(NSUInteger)aCount]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCount:(NSUInteger)aCount]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtMost:(NSUInteger)aCount]

含有指定參數(shù)的選擇器:

  • [[subject should] receive:(SEL)aSelector withArguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector withCount:(NSUInteger)aCount arguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector withCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector withCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCount:(NSUInteger)aCount arguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ...]
  • [[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ...]

示例:

subject = [Cruiser cruiser];[[subject should] receive:@selector(energyLevelInWarpCore:)     andReturn:theValue(42.0f) withCount:2 arguments:theValue(7)];[subject energyLevelInWarpCore:7];float energyLevel = [subject energyLevelInWarpCore:7];[[theValue(energyLevel) should] equal:theValue(42.0f)];

注意你可以將 any()通配符用作參數(shù).如果你只關(guān)心一個方法的部分參數(shù)的值,這回很有用:

id subject = [Robot robot];[[subject should] receive:@selector(speak:afterDelay:whenDone:) withArguments:@"Hello world",any(),any()];[subject speak:@"Hello world" afterDelay:3 whenDone:nil];

期望:通知

  • [[@"MyNotification" should] bePosted];
  • [[@"MyNotification" should] bePostedWithObject:(id)object];
  • [[@"MyNotification" should] bePostedWithUserInfo:(NSDictionary *)userInfo];
  • [[@"MyNotification" should] bePostedWithObject:(id)object andUserInfo:(NSDictionary *)userInfo];
  • [[@"MyNotification" should] bePostedEvaluatingBlock:^(NSNotification *note)block];

Example:

it(@"Notification", ^{    [[@"自定義通知" should] bePosted];        NSNotification *myNotification = [NSNotification notificationWithName:@"自定義通知"                                                                   object:nil];    [[NSNotificationCenter defaultCenter] postNotification:myNotification];});

期望: 異步調(diào)用

  • [[subject shouldEventually] receive:(SEL)aSelector]
  • [[subject shouldEventually] receive:(SEL)aSelector withArguments:(id)firstArgument, ...]

期望: 異常

  • [[theBlock(^{ ... }) should] raise]
  • [[theBlock(^{ ... }) should] raiseWithName:]
  • [[theBlock(^{ ... }) should] raiseWithReason:(NSString *)aReason]
  • [[theBlock(^{ ... }) should] raiseWithName:(NSString *)aName reason:(NSString *)aReason]

示例:

    [[theBlock(^{        [NSException raise:@"FooException" reason:@"Bar-ed"];    }) should] raiseWithName:@"FooException" reason:@"Bar-ed"];

自定義匹配器

Kiwi中,自定義匹配器的最簡單方式是創(chuàng)建KWMatcher的子類,并以適當(dāng)?shù)姆绞街貙懴旅媸纠械姆椒?

為了讓你自定義的匹配器在規(guī)則中可用,你需要在規(guī)則中使用 registerMatchers(namespacePrefix)進行注冊.

看下Kiwi源文件中的匹配器寫法(如KWEqualMatcher等),將會使你受益匪淺.

示例:

    // Snippet from AnimalTypeMatcher.m        #pragma mark Getting Matcher Strings        // REQUIRED: Return an array of selector strings for the expectations this    // matcher is used for.    //    // For example, this matcher handles [[subject should] beTypeOfMammal:] and    // [[subject should] beTypeOfInsect:].    + (NSArray *)matcherStrings {        return [NSArray arrayWithObjects:@"beTypeOfMammal:", @"beTypeOfInsect:", nil];    }        #pragma mark Matching        // REQUIRED: Evaluate the predicate here.    // self.subject is available automatically.    // self.otherSubject is a member variable you would have declared yourself.    - (BOOL)evaluate {        return [[self.subject animalType] isEqual:self.otherSubject];    }        #pragma mark Getting Failure Messages        // REQUIRED: Return a custom error message for when "should" is used.    - (NSString *)failureMessageForShould {        return @"expected subject to be an animal or insect";    }        // OPTIONAL: If you don't override this, Kiwi uses -failureMessageForShould: and    // replaces the first "to" with "not to".    - (NSString *)failureMessageForShouldNot {        return @"expected subject not to be an animal or insect";    }        #pragma mark Configuring Matchers        // These methods should correspond to the selector strings returned in +matcherStrings.    //    // Use them to finish configuring your matcher so that -evaluate can be called    // successfully later. Being a subclass of KWMatcher handles other details like    // setting up self.subject.        - (void)beTypeOfMammal:(id)anObject {      self.otherSubject = anObject;    }        - (void)beTypeOfInsect:(id)anObject {      self.otherSubject = anObject;    }

模擬對象

模擬對象模擬某個類,或者遵循某個寫一個.他們讓你在完全功能完全實現(xiàn)之前,就能更好地專注于對象間的交互行為,并且能降低對象間的依賴--模擬或比避免那些運行規(guī)則時幾乎很難出現(xiàn)的情況.

it(@"Mock", ^{    id carMock = [YFKiwiCar mock];    [ [carMock should] beMemberOfClass:[YFKiwiCar class]];    [ [carMock should] receive:@selector(currentGear) andReturn:theValue(3)];    [ [theValue([carMock currentGear]) should] equal:theValue(3)];        id carNullMock = [YFKiwiCar nullMock];    [ [theValue([carNullMock currentGear]) should] equal:theValue(0)];    [carNullMock applyBrakes];        id flyerMock = [KWMock mockForProtocol:@protocol(YFKiwiFlyingMachine)];    [ [flyerMock should] conformToProtocol:@protocol(YFKiwiFlyingMachine)];    [flyerMock stub:@selector(dragCoefficient) andReturn:theValue(17.0f)];        id flyerNullMock = [KWMock nullMockForProtocol:@protocol(YFKiwiFlyingMachine)];    [flyerNullMock takeOff];});

模擬 Null 對象

通常模擬對象收到一個非預(yù)期的選擇器或消息模式時,會拋出異常(PS:iOS開發(fā)常見錯誤奔潰之一).在模擬對象上使用 stubreceive期望,期望的消息會自動添加到模擬對象上,以實現(xiàn)對方法的模擬.

如果你不關(guān)心模擬對象如何處理其他非預(yù)期的消息,也不想在收到非預(yù)期消息時拋出異常,那就使用 null 模擬對象吧(也即 null 對象).

模擬類的實例

創(chuàng)建類的模擬實例(NSObject 擴展):

  • [SomeClass mock]
  • [SomeClass mockWithName:(NSString *)aName]
  • [SomeClass nullMock]
  • [SomeClass nullMockWithName:(NSString *)aName]

創(chuàng)建類的模擬實例:

  • [KWMock mockForClass:(Class)aClass]
  • [KWMock mockWithName:(NSString *)aName forClass:(Class)aClass]
  • [KWMock nullMockForClass:(Class)aClass]
  • [KWMock nullMockWithName:(NSString *)aName forClass:(Class)aClass]

模擬協(xié)議的實例

創(chuàng)建遵循某協(xié)議的實例:

  • [KWMock mockForProtocol:(Protocol *)aProtocol]
  • [KWMock mockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol]
  • [KWMock nullMockForProtocol:(Protocol *)aProtocol]
  • [KWMock nullMockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol]

存根

存根,能返回指定定選擇器或消息模式的封裝好的請求.Kiwi中,你可以存根真實對象(包括類對象)或模擬對象的方法.沒有指定返回值的存根,將會對應(yīng)返回nil,0等零值.存根需要返回標(biāo)量的,標(biāo)量需要使用 theValue(某個標(biāo)量)宏 裝箱.

所有的存根都會在規(guī)范的一個例子的末尾(一個itblock)被清除.

存根選擇器:

  • [subject stub:(SEL)aSelector]
  • [subject stub:(SEL)aSelector andReturn:(id)aValue]

存根消息模式:

  • [ [subject stub] *messagePattern*]
  • [ [subject stubAndReturn:(id)aValue] *messagePattern*]

示例:

	id cruiser = [Cruiser cruiser];	[ [cruiser stubAndReturn:theValue(42.0f)] energyLevelInWarpCore:7];	float energyLevel = [cruiser energyLevelInWarpCore:7];	[ [theValue(energyLevel) should] equal:theValue(42.0f)];		[Cruiser stub:@selector(classification) andReturn:@"Not a moon"];	[ [ [Cruiser classification] should] equal:@"Not a moon"];		id mock = [Animal mock];	[mock stub:@selector(species) andReturn:@"P. tigris"];	[ [mock.species should] equal:@"P. tigris"];

捕捉參數(shù)

有時,你可能想要捕捉傳遞給模擬對象的參數(shù).比如,參數(shù)可能沒有是一個沒有很好實現(xiàn) isEqual: 的對象,如果你想確認傳入的參數(shù)是否是需要的,那就要單獨根據(jù)某種自定義規(guī)則去驗證.另外一種情況,也是最長遇到的情況,就是模擬對象接收的消息的某個參數(shù)是一個block;通常必須捕捉并執(zhí)行這個block才能確認這個block的行為.

示例:

id robotMock = [KWMock nullMockForClass:[YFKiwiCar class]];KWCaptureSpy *spy = [robotMock captureArgument:@selector(speak:afterDelay:whenDone:) atIndex:2];    [[robotMock should] receive:@selector(speak:) withArguments:@"Goodbye"];    [robotMock speak:@"Hello" afterDelay:2 whenDone:^{    [robotMock speak:@"Goodbye"];}];    void (^block)(void) = spy.argument;block();

存根的內(nèi)存管理問題

未來的某天,你或許需要存根alloc等法官法.這可能不是一個好主意,但是如果你堅持,Kiwi也是支持的.需要提前指出的是,這么做需要深入思考某些細節(jié)問題,比如如何管理初始化.

Kiwi 存根遵循 Objective-C 的內(nèi)存管理機制.當(dāng)存根將返回值寫入一個對象時,如果選擇器是以alloc,或new開頭,或含有 copy時,retain消息將會由存根自動在對象發(fā)送前發(fā)送.

因此,調(diào)用者不需要特別處理由存根返回的對象的內(nèi)存管理問題.

警告

Kiwi深度依賴Objective-C的運行時機制,包括消息轉(zhuǎn)發(fā)(比如 forwardInvocation:).因為Kiwi需要預(yù)先判斷出來哪些方法可以安全調(diào)用.使用Kiwi時,有一些慣例,也是你需要遵守的.

為了使情況簡化和有條理,某些方法/選擇器,是決不能在消息模式中使用,接收期望,或者被存根;否則它們的常規(guī)行為將會被改變.不支持使用這些控制器,而且使用后的代碼的行為結(jié)果也會變的很奇怪.

在實踐中,對于高質(zhì)量的程序代碼,你可能不需要擔(dān)心這些,但是最好還是對這些有些印象.

黑名單(使用有風(fēng)險):

  • 所有不在白名單中的NSObject類方法和NSObject協(xié)議中的方法.(比如-class, -superclass, -retain, -release等.)
  • 所有的Kiwi對象和方法.

白名單(可安全使用):

  • +alloc
  • +new
  • +copy
  • -copy
  • -mutableCopy
  • -isEqual:
  • -description
  • -hash
  • -init
  • 其他任何不在NSObject類或NSobject協(xié)議中的方法.

異步測試

iOS應(yīng)用經(jīng)常有組件需要在后臺和主線程中內(nèi)容溝通.為此,Kiwi支持異步測試;因此就可以進行集成測試-一起測試多個對象.

expectFutureValue() 和 shouldEventually

為了設(shè)置異步測試,你 必須 使用 expectFutureValue 裝箱,并且使用 shouldEventuallyshouldEventuallyBeforeTimingOutAfter來驗證.

shouldEventually 默認在判定為失敗前等待一秒.

[[expectFutureValue(myObject) shouldEventually] beNonNil];

標(biāo)量的處理

當(dāng)主語中含有標(biāo)量時,應(yīng)該使用 expectFutureValue中使用 theValue裝箱標(biāo)量.例如:

[[expectFutureValue(theValue(myBool)) shouldEventually] beYes];

shouldEventuallyBeforeTimingOutAfter()

這個block默認值是2秒而不是1秒.

[[expectFutureValue(fetchedData) shouldEventuallyBeforeTimingOutAfter(2.0)] equal:@"expected response data"];

反轉(zhuǎn)

也有shouldNotEventuallyshouldNotEventuallyBeforeTimingOutAfter 的變體.

一個基于LRResty的示例:

這個block會在匹配器滿足或者超時(默認: 1秒)時完成.

This will block until the matcher is satisfied or it times out (default: 1s)

    context(@"Fetching service data", ^{        it(@"should receive data within one second", ^{            __block NSString *fetchedData = nil;            [[LRResty client] get:@"http://www.example.com" withBlock:^(LRRestyResponse* r) {                NSLog(@"That's it! %@", [r asString]);                fetchedData = [r asString];            }];            [[expectFutureValue(fetchedData) shouldEventually] beNonNil];        });    });

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 建宁县| 通化市| 丰都县| 金寨县| 金塔县| 咸宁市| 陇南市| 通江县| 清流县| 三亚市| 英超| 上饶市| 白朗县| 安达市| 儋州市| 鹤山市| 昌都县| 阿拉善盟| 邻水| 百色市| 罗田县| 扎兰屯市| 修水县| 马鞍山市| 和田市| 湛江市| 抚顺市| 泊头市| 裕民县| 恩施市| 盱眙县| 化隆| 盱眙县| 威海市| 阜康市| 乌恰县| 灵山县| 丰顺县| 云阳县| 西乌珠穆沁旗| 万盛区|