【问题标题】:How to stub out the return value on a class based method using ocmock如何使用 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 是否支持此功能,如果不支持,我现在应该如何解决此问题。

【问题讨论】:

    标签: iphone objective-c ocunit 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
    【解决方案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 ...
    }
    

    【讨论】:

      【解决方案3】:

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

      可行,但复杂。

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

      【讨论】:

        【解决方案4】:

        OCMock 支持此功能:

        [[[[mockLocationManagerClass stub] classMethod] andReturnValue:OCMOCK_VALUE(YES)] locationServicesEnabled];
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          • 2013-04-24
          • 1970-01-01
          • 2015-06-19
          • 1970-01-01
          • 2017-05-12
          相关资源
          最近更新 更多