2015-08-15 68 views
0

我正在從視圖控制器中編寫performLogin方法的測試,我無法重構原始方法,也無法重構原始方法中的任何其他文件項目。我只能修改我的LoginTest.m文件。我正在嘗試使用OCMock來完成此測試,而不更改原始程序文件。從視圖控制器的方法內部調用UIAlertView作爲類方法的測試/嘲弄

我想驗證,如果performLogin方法收到異常顯示UIAlertView。 ViewHelper類有一個返回void的類方法,並創建並顯示一個UIAlertView。

我嘲笑調用ViewHelper的視圖控制器,並試圖使用OCMock使用來自各種搜索的示例測試UIAlertView類,但是由於它不捕獲AlertView的創建,所以這些測試失敗。

我是新的使用OCMock,我不知道如何模擬這種類型的測試ViewHelper文件,特別是它從方法內調用,而不是錨定到屬性。

有可能是一個簡單的方法來做到這一點,但我還沒有弄明白,任何幫助將不勝感激。

在此先感謝。

在調用doLogin的方法內部,它返回TRUE或引發異常。當拋出異常時,它應該彈出一個帶有消息的UIAlertView。我想測試代碼的這一部分並確認UIAlertView實際顯示。

請注意,ViewHelper是直接從catch塊中調用的。

的方法是這樣的:

-(IBAction)performLogin:(id)sender { 
    if(valid) 
    { 
    @try 
    { 
     //loginModel is a private instance variable 
     loginModel = [[Services sharedInstance] getLoginModel]; 
     [loginModel doLoginWithUsername:username andPassword:password]; 
     [self performSegueWithIdentifier:@"showWelcomeScreen" sender:self]; 
    } 
    @catch(NSException *e) 
    { 

     //I NEED TO TEST THAT THIS CALL OCCURS AND THE UIAlertView is shown 
     //Not sure how to do this as it's not anchored to a property or method 
     //and is called as a class method 

     [ViewHelper showAlertForTitle:"Error" andTheMessage:@"network error" 
     andAccessibilityLabel:@"Network Error"]; 
    } 
    } 
} 

的ViewHelpder.m看起來是這樣的 - 請注意,它的一類方法:

+(void) showAlertForTitle:(NSString *)title andTheMessage:(NSString *)message  andAccessibilityLabel:(NSString *)label 
{ 
    NSLog(@"reached alert notification class"); 
    UIAlertView *successAlert = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
    [successAlert setAccessibilityLabel:label]; 
    [successAlert show]; 
} 

別的不說是我到目前爲止已經試過:

- (void)testForfailedLogin { 

    //call singleton first to initialize mocked login model 
    [[Services sharedInstance] getLoginModel]; 

    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]; 


    //set the actual IBOutlets with login values designed to sucessfully login 
    self.controller.usernameTextField.text = @"error"; 
    self.controller.passwordTextField.text = @"error"; 

    //call the login process to verify the login method 
    [self.controller performLogin:nil]; 

    [mockAlertView verify]; 
    [mockAlertView stopMocking]; 

}

回答

1

我不記得OCMock API已經如此肯定這不會編譯,但我相信這個想法是正確的。 (我基本上跟着this guide

首先,最好用兩個測試來測試這個。 第一個測試將僅檢查在異常時間被調用的[ViewHelper showAlertForTitle:"Error" andTheMessage:@"network error" andAccessibilityLabel:@"Network Error"]。當+[ViewHelper showAlertForTitle:andTheMessage:andAccessibilityLabel:]被調用時,第二個測試將檢查出現的警報視圖(或任何其他想要發生的事情)。

第一個測試(重要部件):

// ① You need to setup something to raise an exception 
// so the catch block will be called 
// Probably you want to use [OCMArg any] or [OCMArg anyPointer] 
OCMStub([mockServices loginWithUsername:[OCMArg isNotNil] 
          andPassword:[OCMArg isNotNil]]) 
    .andThrow(anException); 

// ② Call your method 
// sender can be nil in your code. 
[controller performLogin:sender]; 

// ③ Expect your method to be called 
OCMExpect([mockViewHelper showAlertForTitle:"Error" 
           andTheMessage:@"network error" 
         andAccessibilityLabel:@"Network Error"]); 

// ④ Verify 
OCMVerifyAll(mockViewHelper) 

第二次測試的要點:

// ① Stub everything you want to check to be called inside 
// `+[ViewHelper showAlertForTitle:andTheMessage:andAccessibilityLabel:]` 
// probably your UIAlertView, etc 
id mockAlertView = ... 

// ② Call your method 
[ViewHelper showAlertForTitle:@"something" 
       andTheMessage:@"something" 
     andAccessibilityLabel:@"something"]; 

// ③ Expect your stuff to be called 
OCMExpect([mockAlertView someMethod]); 

// ④ Verify 
OCMVerifyAll(mockAlertView) 

最後一兩件事,在objc世界例外用於編程錯誤,如果你正在設計這些類,考慮像許多其他可可API一樣返回一個NSError。

+0

感謝您的幫助,就引發我需要什麼來解決我的問題。 – CocoaEv

2

正如nacho4d的回答中所提到的,這有兩個部分:使登錄模型拋出異常並驗證控制器是否顯示錯誤。

鑑於你的描述我會使用一個模擬部分爲登錄模式,拋出異常的約束:

id modelMock = OCMPartialMock([[Services sharedInstance] getLoginModel]); 
OCMStub([modelMock doLoginWithUsername:[OCMArg any] andPassword:[OCMArg any]]) 
    .andThrow([NSException exceptionWithName:@"TestException" reason:@"Test" userInfo:nil]); 

要檢查面板顯示我不會與視圖打擾。我會簡單地檢查,在助手類方法是從performLogin:實現調用:

id viewHelperMock = OCMClassMock([ViewHelper class]); 
[controller performLogin:sender]; 
OCMVerify([viewHelperMock showAlertForTitle:"Error" andTheMessage:@"network error" andAccessibilityLabel:@"Network Error"]); 
+0

謝謝。我最初的問題是我的例外設置很好,但我忘記了包含doLogin調用的用戶名/密碼驗證檢查。我不得不做一個partialMock,並將這個調用存根直接返回true,以允許調用doLogin。一旦到達那裏,我就可以微調真正價值或異常的回報。 – CocoaEv

+0

直接從OCMock的創建者那裏得到建議真是太好了:) – nacho4d