0

我正在编写一个测试来验证位置服务是否在单击按钮时启动。这需要一个非常简单的 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];
    }
}

除了该方法在 iOS 4.0 中已弃用之外,一切都很好。所以现在我需要改用类方法 [CLLocationManager locationServicesEnabled]。

问题是我似乎无法找到 ocmock 是否支持此功能,如果不支持,我现在应该如何解决这个问题。

4

4 回答 4

3

嗯,你可以使用methodExchange。只需确保在完成后将方法交换回原始方法即可。看起来很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

编辑:我以为您正在验证,但似乎您正在存根。更新代码

于 2012-03-26T01:13:27.523 回答
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 ...
}
于 2012-03-26T19:26:31.993 回答
1

我认为不会。我能想到的唯一方法是使用部分模拟,然后使用运行时调用来调动您需要的实现。

可行,但复杂。

一个更面向模式的解决方案可能是将位置服务的检查提取出来放在协议后面。然后,您可以在测试期间简单地对协议的实现使用模拟,以根据您的要求返回 YES 或 NO。由于实际的实现只会返回[CLLocationManager locationServicesEnabled],因此您可以不测试它而侥幸逃脱。

于 2012-03-26T01:13:14.783 回答
0

OCMock 支持此功能:

[[[[mockLocationManagerClass stub] classMethod] andReturnValue:OCMOCK_VALUE(YES)] locationServicesEnabled];
于 2015-01-21T22:16:18.370 回答