7

我正在浏览一个应用程序并添加单元测试。该应用程序使用故事板编写,支持 iOS 6.1 及更高版本。

我已经能够毫无问题地测试所有常用的返回方法。但是,我目前对要执行的某个测试感到困惑:

本质上我有一个方法,我们称之为doLogin:

- (IBAction)doLogin:(UIButton *)sender {

// Some logic here

if ( //certain criteria to meet) {
    variable = x; // important variable set here
    [self performSegueWithIdentifier:@"memorableWord" sender:sender];
} else {
    // handler error here
}

所以我想测试是否调用了 segue 并设置了变量,或者加载了 MemorableWord 视图控制器并且其中的变量是正确的。在 doLogin 方法中设置的变量在 prepareForSegue 方法中被传递到 rememberWord segues 的目标视图控制器。

我有 OCMock 设置和工作,我也使用 XCTest 作为我的单元测试框架。有没有人能够生产一个单元测试来涵盖这种情况?

似乎 Google 和 SO 在有关该领域的信息方面非常赤裸裸。许多关于简单基本测试的示例与更复杂的 iOS 测试现实完全无关。

4

4 回答 4

4

你在正确的轨道上,你的测试想要检查:

  1. 当登录按钮被点击时,doLogin 被调用,loginButton 作为发送者
  2. 如果某些条件为 YES,则调用 performSegue

因此,您实际上应该触发从登录按钮向下到 performSegue 的完整流程:

- (void)testLogin {
    LoginViewController *loginViewController = ...;
    id loginMock = [OCMockObject partialMockForObject:loginViewController];

    //here the expect call has the advantage of swallowing performSegueWithIdentifier, you can use forwardToRealObject to get it to go all the way through if necessary
    [[loginMock expect] performSegueWithIdentifier:@"memorableWord" sender:loginViewController.loginButton];

    //you also expect this action to be called
    [[loginMock expect] doLogin:loginViewController.loginButton];

    //mocking out the criteria to get through the if statement can happen on the partial mock as well
    BOOL doSegue = YES;
    [[[loginMock expect] andReturnValue:OCMOCK_VALUE(doSegue)] criteria];

    [loginViewController.loginButton sendActionsForControlEvents:UIControlEventTouchUpInside];

    [loginMock verify]; [loginMock stopMocking];
}

您需要为“标准”实现一个属性,以便有一个可以使用“期望”模拟的吸气剂。

重要的是要意识到'expect'只会模拟对getter的1次调用,后续调用将失败并显示“调用了意外的方法......”。您可以使用“存根”为所有调用模拟它,但这意味着它将始终返回相同的值。

于 2013-12-26T06:55:20.533 回答
2

恕我直言,这似乎是一个not properly已经设置好的测试场景。

对于单元测试,您应该只test units(例如,单一方法)您的应用程序。这些单元应该independent来自应用程序的所有其他部分。这将保证您正确测试单个功能而没有任何副作用。顺便说一句:OCMock是“模拟”您不想测试的所有部分并因此产生副作用的好工具。

一般来说,您的测试似乎更像是集成测试

IT is the phase of software testing, in which individual software modules are combined and tested as a group.

那么在你的情况下我会怎么做:

我要么定义一个集成测试,在那里我将正确测试我的视图的所有部分,因此间接测试我的视图控制器。看看这种场景的一个很好的测试框架 - KIF

或者我将对方法“doLogin”以及计算 if 语句中的条件的方法执行单个单元测试。所有依赖项都应该被模拟出来,这意味着在你的 doLogin 测试中,你甚至应该模拟标准方法......

于 2013-12-16T11:29:38.293 回答
2

所以我能看到对我进行单元测试的唯一方法是使用部分模拟:

- (void)testExample
{
    id loginMock = [OCMockObject partialMockForObject:self.controller];

    [[loginMock expect] performSegueWithIdentifier:@"memorableWord" sender:[OCMArg any]];

    [loginMock performSelectorOnMainThread:@selector(loginButton:) withObject:self.controller.loginButton waitUntilDone:YES];

    [loginMock verify];
}

当然,这只是测试的一个示例,实际上并不是我正在执行的测试,但希望能演示我必须在视图控制器中测试此方法的方式。如您所见,如果performSegueWithIdentifier未调用 ,则验证会导致测试失败。

于 2013-12-16T12:39:15.927 回答
1

给 OCMock 读一读,我刚从亚马逊买了一本关于 iOS 单元测试的书,它真的很好读。也想买一本 TDD 书。

于 2014-09-04T15:53:16.270 回答