如何使用ocmock在基于类的方法上存根返回值

时间:2012-03-25 21:53:51

标签: iphone objective-c ocunit ocmock

我正在编写一个测试来验证按钮点击发生时是否启动了位置服务。这需要一个非常简单的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是否支持此功能,如果不支持,我现在应该如何解决这个问题。

4 个答案:

答案 0 :(得分:3)

嗯,你可以使用methodExchange。只需确保在完成后将方法更换回原始方法。看起来很hacky,但我还没有找到更好的解决方案。我已经做了类似的事情[NPDate 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

编辑:我以为你在验证,但好像你在瞎扯。更新的代码

答案 1 :(得分:2)

最简单的方法是覆盖单元测试类中某个类别中的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 ...
}

答案 2 :(得分:1)

我不认为这样。我能想到的唯一方法是使用部分mock,然后使用运行时调用来调整你需要的实现。

可行,但很复杂。

更多面向模式的解决方案可能是提取位置服务的检查以支持协议。然后,您可以在测试期间使用模拟执行协议的实现,以根据需要返回YES或NO。由于实际的实现除了返回[CLLocationManager locationServicesEnabled]之外什么都不做,你可以逃脱而不测试它。

答案 3 :(得分:0)

这是OCMock支持的:

[[[[mockLocationManagerClass stub] classMethod] andReturnValue:OCMOCK_VALUE(YES)] locationServicesEnabled];
相关问题