如何使用 ocmock 在基于类的方法上存根返回值
Posted
技术标签:
【中文标题】如何使用 ocmock 在基于类的方法上存根返回值【英文标题】:How to stub out the return value on a class based method using ocmock 【发布时间】:2012-03-25 21:53:50 【问题描述】:我正在编写一个测试来验证位置服务是否在单击按钮时启动。这需要一个非常简单的 if 语句来确保手机有可用的定位服务。
现在的工作测试如下所示
- (void)testStartUpdatingLocationInvokedWhenLocationServicesAreEnabled
[[[self.locationManager stub] andReturnValue:[NSNumber numberWithBool:true]] locationServicesEnabled];
[[self.locationManager expect] startUpdatingLocation];
[self.sut buttonClickToFindLocation:nil];
[self.locationManager verify];
现在测试的实现如下所示
- (IBAction)buttonClickToFindLocation:(id)sender
if ([self.locationManager locationServicesEnabled])
[self.locationManager startUpdatingLocation];
除了方法是deprecated in ios 4.0之外,一切都很好。所以现在我需要使用类方法 [CLLocationManager locationServicesEnabled]。
问题是我似乎无法找到 ocmock 是否支持此功能,如果不支持,我现在应该如何解决此问题。
【问题讨论】:
【参考方案1】:嗯,你可以使用方法交换。只需确保在完成后将方法交换回原始方法即可。看起来很hacky,但我还没有找到更好的解决方案。我为存根 [NSDate date] 做了类似的事情
@implementation
static BOOL locationManagerExpectedResult;
- (void)testStartUpdatingLocationInvokedWhenLocationServicesAreEnabled
locationManagerExpectedResult = YES;
method_exchangeImplementations(
class_getClassMethod([CLLocationManager class], @selector(locationServicesEnabled)) ,
class_getClassMethod([self class], @selector(locationServicesEnabledMock))
);
[self.sut buttonClickToFindLocation:nil];
+ (BOOL)locationServicesEnabledMock
return locationManagerExpectedResult;
@end
编辑:我以为你在验证,但看起来你在存根。更新代码
【讨论】:
对于其他遵循 -note- 的人,您需要添加此导入 #import最简单的方法是在单元测试类的一个类别中覆盖locationServicesEnabled
:
static BOOL locationServicesEnabled = NO;
@implementation CLLocationManager (UnitTests)
+(BOOL)locationServicesEnabled
return locationServicesEnabled;
@end
...
-(void)tearDown
// reset to default after each test
locationServicesEnabled = NO;
[super tearDown];
它只会在测试时覆盖超类方法,并且您可以在每个测试中将静态全局设置为适当的值。
或者,您可以将检查包装在您自己的实例方法中,并使用部分模拟。
在被测类中:
-(BOOL)locationServicesEnabled
return [CLLocationManager locationServicesEnabled];
在你的测试中:
-(void)testSomeLocationThing
MyController *controller = [[MyController alloc] init];
id mockController = [OCMockObject partialMockForObject:controller];
BOOL trackingLocation = YES;
[[[mockController stub] andReturnValue:OCMOCK_VALUE(trackingLocation)] locationServicesEnabled];
// test your controller ...
【讨论】:
【参考方案3】:我认为不会。我能想到的唯一方法是使用部分模拟,然后使用运行时调用来调动您需要的实现。
可行,但复杂。
一个更面向模式的解决方案可能是将位置服务检查提取到协议后面。然后,您可以在测试期间简单地对协议的实现使用模拟,以根据您的要求返回 YES 或 NO。由于实际实现只会返回[CLLocationManager locationServicesEnabled]
,因此您可以不对其进行测试。
【讨论】:
【参考方案4】:OCMock 支持此功能:
[[[[mockLocationManagerClass stub] classMethod] andReturnValue:OCMOCK_VALUE(YES)] locationServicesEnabled];
【讨论】:
以上是关于如何使用 ocmock 在基于类的方法上存根返回值的主要内容,如果未能解决你的问题,请参考以下文章