我正在浏览一个应用程序并添加单元测试。该应用程序使用故事板编写,支持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方法中的memorableWord segues'目标视图控制器。
我有OCMock设置和工作,我也使用XCTest作为我的单元测试框架。有没有人能够进行单元测试以涵盖这种情况?
似乎Google和SO在这个领域的信息方面相当简单。很多关于简单基本测试的例子与iOS测试中更复杂的现实无关。
答案 0 :(得分:4)
你走在正确的轨道上,你的考试要检查:
因此,您实际上应该从登录按钮触发完整流程以执行Segue:
- (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。
重要的是要意识到'expect'只会模拟对getter的1次调用,后续调用将失败并显示“调用了意外的方法......”。
可以使用'stub'来模拟所有调用,但这意味着它将始终返回相同的值。答案 1 :(得分:2)
恕我直言,这似乎是一个已设置not properly
的测试场景。
使用 单元测试 ,您应该只应用test units
(例如单个方法)。这些单位应该是independent
来自您申请的所有其他部分。这将保证您正确测试单个功能,没有任何副作用。
BTW:OCMock
是“模拟”你不想测试的所有部分并因此产生副作用的好工具。
一般来说,您的测试似乎更像是 集成测试
IT is the phase of software testing, in which individual software modules are combined and tested as a group
。
那么在你的情况下我会做什么:
我要么定义一个集成测试,我会正确测试视图的所有部分,从而间接测试我的视图控制器。看看这种场景的良好测试框架 - KIF
或者我会对方法'doLogin'执行单个单元测试,以及在if语句中计算标准的方法。所有依赖项都应该被模拟掉,这意味着在你的doLogin测试中,你甚至应该模拟标准方法......
答案 2 :(得分: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
,则验证会导致测试失败。
答案 3 :(得分:1)
给OCMock一个阅读,我刚从亚马逊那里购买了一本关于iOS测试单元的书,它非常适合阅读。希望得到一本TDD书。