2011-05-21 65 views
15

你如何編寫急救單元測試?iOS單元測試:如何設置/更新/檢查firstResponder?

我想寫一個測試來確認一種方法將焦點推進到下一個文本字段。 controllerUIViewController的後裔。但是這個探索性測試失敗了:

- (void)testFirstResponder 
{ 
    [controller view]; 
    [[controller firstTextField] becomeFirstResponder]; 

    STAssertTrue([[controller firstTextField] isFirstResponder], nil); 
} 

第一行導致視圖被加載,因此它的插口就位。文本字段不爲零。但測試永遠不會通過。

我猜becomeFirstResponder沒有立即設置第一響應者,但安排它以備後用。那麼是否有一種很好的方式來針對它進行單元測試?

從評論拉起答案接受的答案...讓事情短時間運行:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]; 

回答

7

我猜管理/改變第一響應者鏈以某種方式在主迴路來完成,當UI被更新爲下一個事件處理做準備。如果這個假設是正確的,我只想做到以下幾點:

-(void)assertIfNotFirstResponder:(UITextField*)field { 
    STAssertTrue([field isFirstResponder], nil); 
} 

- (void)testFirstResponder 
{ 
    [controller view]; 
    [[controller firstTextField] becomeFirstResponder]; 
    [self performSelector:@selector(@"assertIfNotFirstResponder:") withObject:[controller firstTextField] afterDelay:0.0]; 
} 

注:我已經使用0.0延遲,因爲我只是想該消息被放在事件隊列,並儘快出動。我需要一種方式來回到主循環,以便進行家務管理。這不應該在你的情況下產生實際的延遲。如果您正在執行多種相同類型的測試,即通過反覆更改作爲第一響應者的控件,此技術應確保所有這些事件都正確地與performSelector生成的事件一起排序。

如果從不同的線程中運行你的測試,你可以使用– performSelectorOnMainThread:withObject:waitUntilDone:

+1

好的想法,但對於單元測試,在事件隊列中放置消息不起作用,因爲測試結束並且在斷言之前夾具被拆除。反例:將'STAssertTrue'改爲'STAssertFalse'沒有什麼區別。 – 2011-05-29 23:26:40

+1

對,這不起作用...作爲進一步的提示,我剛剛發現你可以在測試條件之前運行一段時間的運行循環:'[[NSRunLoop currentRunLoop] runUntilDate:fiveSecondsFromNow];'(from: http://stackoverflow.com/questions/1077737/ocunit-test-for-protocols-callbacks-delegate-in-objective-c)。我不完全相信這個解決方案真的很「優雅」,但它應該可以工作,並且可能適合「較小」的情況(當你不需要跟弗蘭克一起去時)... – sergio 2011-05-30 11:55:45

+0

非常棒!即使指定一個零的運行時間也有訣竅。 – 2011-05-30 23:42:14

6

您需要確保文本字段被安裝在視圖層次結構。

如果視圖的窗口屬性包含UIWindow對象,則它已安裝在視圖層次結構中;如果它返回nil,則該視圖將從任何層次結構中分離出來。

希望這有助於....

+0

你是對的,UIWindow是零。有沒有辦法在單元測試中改變它?推視圖控制器是行不通的,因爲它調度了一些東西,但是(如我對@sergio的迴應所指出的)不足以進行單元測試。 – 2011-05-29 23:30:58

+0

我現在有我的答案。 +1填補部分難題。 – 2011-05-31 01:06:54

2

這似乎不是單元測試,這對於自動化驗收測試工作。 (見Frank: Automated Acceptance Tests for iPhone and iPad

+0

+1這樣一個美好的框架! – sergio 2011-05-30 11:49:49

+0

我同意,不幸的是,據我所知,到目前爲止Xcode新的UI測試框架無法做到這一點。 – 2015-11-26 16:41:37

15

使用的Xcode 5.1和XCTestCase,這似乎好工作:

- (void)testFirstResponder 
{  
    // Make sure the controller's view has a window 
    UIWindow *window = [[UIWindow alloc] init]; 
    [window addSubview:controller.view]; 

    // Call whatever method you're testing 
    [controller.textView becomeFirstResponder]; 

    // Assert that the desired subview is the first responder 
    XCTAssertTrue([sut.textView isFirstResponder]); 
} 

爲了使視圖/子視圖成爲第一個響應者,它必須是一個視圖層次的一部分,這意味着它的根視圖的窗口屬性必須設置

Jon和Sergio提到您在需要的子視圖上致電becomeFirstResponder後可能需要致電[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]],但我發現在我們的例子中這不是必需的。

但是,您的里程可能會有所不同(甚至取決於您使用的Xcode版本),因此您可能需要也可能不需要包含此類調用。

+0

您是否覺得這個作品沒有把窗口變成關鍵窗口? – bejonbee 2015-08-18 14:56:15

+0

從iOS 10.3開始,此工作不會成爲關鍵窗口。 – XML 2017-07-13 10:32:39