2015-08-15 65 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