[英]OCMock of class with method called from the test method
我正在尝试测试实例化MFMailComposeViewController
实例的方法。 被测试的方法调用MFMailComposeViewController
几种方法,包括setSubject:
我想测试是否向setSubject发送了特定的NSString,在这种情况下为@“ Test Message”。
无论我在模拟存根中为期望的字符串指定什么内容,都不会失败。
在单元测试类中:
#import <OCMock/OCMock.h>
- (void)testEmail {
TestClass *testInstance = [[TestClass alloc] init];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock stub] setSubject:@"Test Message"];
[testInstance testMethod];
}
在TestClass中:
- (void)testMethod {
MFMailComposeViewController *mailComposeVC = [[MFMailComposeViewController alloc] init];
[mailComposeVC setSubject:@"Bad Message"];
}
Test Suite 'Email_Tests' started at 2011-09-17 18:12:21 +0000
Test Case '-[Email_Tests testEmail]' started.
Test Case '-[Email_Tests testEmail]' passed (0.041 seconds).
测试应该失败了。
我正在iOS模拟器中对此进行测试,并在设备上获得相同的结果。
我究竟做错了什么? 有什么办法可以做到这一点?
您创建了一个模拟,但绝不将其传递给测试中的类,或者让该模拟进行验证。 您需要某种形式的依赖项注入来说:“而不是使用MFMailComposeViewController,请使用我给您的另一件事。”
这是一种方法。 在被测类中,不要直接分配MFMailComposeViewController,而是通过工厂方法来获取它,如下所示:
@interface TestClass : NSObject
- (void)testMethod;
// Factory methods
+ (id)mailComposeViewController;
@end
这是实现。 您正在泄漏,因此请注意factory方法将返回一个自动释放的对象。
- (void)testMethod {
MFMailComposeViewController *mailComposeVC =
[[self class] mailComposeViewController];
[mailComposeVC setSubject:@"Bad Message"];
}
+ (id)mailComposeViewController {
return [[[MFMailComposeViewController alloc] init] autorelease];
}
在测试方面,我们创建了一个覆盖工厂方法的测试子类,因此它提供了我们想要的一切:
@interface TestingTestClass : TestClass
@property(nonatomic, assign) id mockMailComposeViewController;
@end
@implementation TestingTestClass
@synthesize mockMailComposeViewController;
+ (id)mailComposeViewController {
return mockMailComposeViewController;
}
@end
现在我们准备好进行测试了。 我做了一些不同的事情:
这是测试:
- (void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:@"Test Message"];
[testInstance setMockMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
}
为了完整起见,我们需要一个最终测试,这是为了确保实际类中的工厂方法返回我们期望的结果:
- (void)testMailComposerViewControllerShouldBeCorrectType {
STAssertTrue([[TestClass mailComposeViewController]
isKindOfClass:[MFMailComposeViewController class]], nil);
}
乔恩·里德(Jon Reid)的方法是一种合理的方法,尽管将mailComposeViewController
类方法似乎使它变得复杂。 在测试代码中将其子类化意味着您将始终在测试时获得模拟版本,而这可能并不是您想要的。 我将其作为实例方法。 然后,您可以在测试时使用部分模拟覆盖它:
-(void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:@"Test Message"];
id mockInstance = [OCMockObject partialMockForObject:testInstance];
[[[mockInstance stub] andReturn:mock] mailComposeViewController];
[testInstance testMethod];
[mock verify];
}
如果将其保留为类方法,则可以考虑使其成为静态全局变量,并提供一种覆盖它的方法:
static MFMailComposeViewController *mailComposeViewController = nil;
-(id)mailComposeViewController {
if (!mailComposeViewController) {
mailComposeViewController = [[MFMailComposeViewController alloc] init];
}
return mailComposeViewController;
}
-(void)setMailComposeViewController:(MFMailComposeViewController *)controller {
mailComposeViewController = controller;
}
然后,您的测试将类似于乔恩的示例:
-(void)testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:@"Test Message"];
[testInstance setMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
// clean up
[testInstance setMailComposeViewController:nil];
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.