我正在研究一款應用程序,只有在遊戲進度已經達到時纔會按下退出按鈕纔會顯示UIAlertView
。我想知道如何使用OCUnit攔截UIAlertView
並與之交互,甚至可以檢測它是否已經呈現。我唯一能想到的就是monkypatch [UIAlertViewDelegate willPresentAlertView]
,但這讓我想哭。使用OCUnit測試是否存在UIAlertView
有沒有人知道這樣做的更好方法?
我正在研究一款應用程序,只有在遊戲進度已經達到時纔會按下退出按鈕纔會顯示UIAlertView
。我想知道如何使用OCUnit攔截UIAlertView
並與之交互,甚至可以檢測它是否已經呈現。我唯一能想到的就是monkypatch [UIAlertViewDelegate willPresentAlertView]
,但這讓我想哭。使用OCUnit測試是否存在UIAlertView
有沒有人知道這樣做的更好方法?
更新:請參見我的博客文章http://qualitycoding.org/testing-alerts/
的問題與我的其他答案是:-showAlertWithMessage:
方法本身是從來沒有單元測試行使。 「使用手動測試來驗證一次」對於簡單的場景來說不是太糟糕,但是錯誤處理通常涉及難以複製的異常情況。 ...另外,我感覺到我已經停止了短暫的嘮叨感覺,並且可能會有更徹底的方式。有。
在被測試的類中,不要直接實例化UIAlertView
。相反,定義方法
+ (Class)alertViewClass
{
return [UIAlertView class];
}
可以使用「子類和覆蓋」進行替換。 (可替代地,使用依賴注入和在作爲初始化參數傳遞這個類。)
調用此,以確定該類實例來顯示警報:
Class alertViewClass = [[self class] alertViewClass];
id alert = [[alertViewClass alloc] initWithTitle:...etc...
現在定義一個模擬警報視圖類。它的任務是要記住它的初始化參數,並張貼通知,傳遞本身作爲對象:
- (void)show
{
[[NSNotificationCenter defaultCenter] postNotificationName:MockAlertViewShowNotification
object:self
userInfo:nil];
}
您的測試子類(TestingFoo)重新定義+alertViewClass
來替代模擬:
+ (Class)alertViewClass
{
return [MockAlertView class];
}
使測試級通知登記。被調用的方法現在可以驗證傳遞給警報初始值設定項的參數以及-show
消息的次數。
附加尖端:除了模擬警報,我所定義的警報驗證類:
因此,我現在所做的所有警報測試都是創建驗證者,設置期望值以及行使電話。
注意:請參閱我的其他答案。我推薦它在這一個。
在實際的類,定義了一個簡短的方法來顯示一個警告,是這樣的:
- (void)showAlertWithMessage:(NSString message *)message
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:message
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
爲您的測試,沒有測試這種方法實際。相反,使用「子類和重寫」來定義一個簡單記錄它的調用和參數的間諜。假設原來的班級名爲「Foo」。下面是用於測試的子類:
@interface TestingFoo : Foo
@property(nonatomic, assign) NSUInteger countShowAlert;
@property(nonatomic, retain) NSString *lastShowAlertMessage;
@end
@implementation TestingFoo
@synthesize countShowAlert;
@synthesize lastShowAlertMessage;
- (void)dealloc
{
[lastShowAlertMessage release];
[super dealloc];
}
- (void)showAlertWithMessage:(NSString message *)message
{
++countShowAlert;
[self setLastShowAlertMessage:message];
}
@end
現在只要
-showAlertWithMessage:
而不是直接顯示警告,並TestingFoo
,而不是Foo
,您可以檢查顯示警報的呼叫數量和最後一條消息。
由於這不會執行顯示警報的實際代碼,因此請使用手動測試來驗證一次。
如果您的Obj C類已經是測試框架的一個子類,例如,您可以執行子類並覆蓋範例工作。 SenTestCase?唯一的選擇是依賴注入嗎? – 2012-04-12 10:27:34
我不明白爲什麼你不應該能夠自定義你自己定義的任何具體類,以覆蓋特定的方法。 – 2012-04-16 01:08:08
啊我想我已經明白了。感謝你。 – 2012-04-16 07:57:29
通過交換UIAlertView的'show'實現,您可以完全無縫地獲得警報視圖的單元測試。例如,這個接口給你提供測試能力的一些量:
@interface UIAlertView (Testing)
+ (void)skipNext;
+ (BOOL)didSkip;
@end
本實施
#import <objc/runtime.h>
@implementation UIAlertView (Testing)
static BOOL skip = NO;
+ (id)alloc
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method showMethod = class_getInstanceMethod(self, @selector(show));
Method show_Method = class_getInstanceMethod(self, @selector(show_));
method_exchangeImplementations(showMethod, show_Method);
});
return [super alloc];
}
+ (void)skipNext
{
skip = YES;
}
+ (BOOL)didSkip
{
return !skip;
}
- (void)show_
{
NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]);
if (skip) {
skip = NO;
return;
}
}
@end
可以例如編寫單元測試像這樣:
[UIAlertView skipNext];
// do something that you expect will give an alert
STAssertTrue([UIAlertView didSkip], @"Alert view did not appear as expected");
如果你想在警報視圖中自動點擊一個特定的按鈕,你將需要更多的魔力。接口增加了兩個新的類方法:
@interface UIAlertView (Testing)
+ (void)skipNext;
+ (BOOL)didSkip;
+ (void)tapNext:(NSString *)buttonTitle;
+ (BOOL)didTap;
@end
這是這樣的
static NSString *next = nil;
+ (void)tapNext:(NSString *)buttonTitle
{
[next release];
next = [buttonTitle retain];
}
+ (BOOL)didTap
{
BOOL result = !next;
[next release];
next = nil;
return result;
}
和表演方法成爲
- (void)show_
{
if (next) {
NSLog(@"UIAlertView :: simulating alert for tapping %@", next);
for (NSInteger i = 0; i < [self numberOfButtons]; i++)
if ([next isEqualToString:[self buttonTitleAtIndex:i]]) {
[next release];
next = nil;
[self alertView:self clickedButtonAtIndex:i];
return;
}
return;
}
NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]);
if (skip) {
skip = NO;
return;
}
}
這可以類似的測試,但不是向下跳讀你」 d說哪個按鈕可以點擊。例如。
[UIAlertView tapNext:@"Download"];
// do stuff that triggers an alert view with a "Download" button among others
STAssertTrue([UIAlertView didTap], @"Download was never tappable or never tapped");
最新版本的OCMock(2.2.1在撰寫本文時)具有使這一切變得簡單的功能。下面是一些示例測試代碼,它存根UIAlertView的「alloc」類方法返回一個模擬對象,而不是真正的UIAlertView。
id mockAlertView = [OCMockObject mockForClass:[UIAlertView class]];
[[[mockAlertView stub] andReturn:mockAlertView] alloc];
(void)[[[mockAlertView expect] andReturn:mockAlertView]
initWithTitle:OCMOCK_ANY
message:OCMOCK_ANY
delegate:OCMOCK_ANY
cancelButtonTitle:OCMOCK_ANY
otherButtonTitles:OCMOCK_ANY, nil];
[[mockAlertView expect] show];
[myViewController doSomething];
[mockAlertView verify];
謝謝!男人,我記得在我學習過關於嘲笑之前測試過,那些是黑暗的年齡。 – wjl 2013-11-21 20:34:11
我可以使用相同的方法來發送郵件作曲嗎?查看我的問題:http://stackoverflow.com/questions/21968556/ocmock-unexpected-method-invoked-though-expected – 2014-02-23 14:27:37
This works great! – vincentjames501 2014-02-26 19:43:08
我覺得在這裏上課會更有用。如果我的單元測試框架阻礙了我的真實代碼,我會感到噁心。我可以編寫一個UIAlertViewPoser,在顯示警告時設置一個標誌。 http://www.cocoadev.com/index.pl?ClassPosing – wjl 2012-03-28 07:29:55
@wjlafrance有趣的,如果它有效,會很好。不幸的是,poseAsClass:在Mac上已被棄用,並且在iOS上從不支持。 – 2012-04-02 01:35:54
是的,我注意到我發佈後不久。呵呵.. :( – wjl 2012-04-10 18:00:41