在ObjectiveC单元测试中模拟系统日期和位置的最佳实践



我正试图使用TDD方法在Objective C中编写一个类。这个类基本上应该实现这个协议。

@protocol SeasonService <NSObject>
-(t_Season)currentSeason;
@end

t_Season只是冬季、春季、夏季和秋季的枚举。

假设我有一个实现上面的类,下面的伪代码

@implementation SeasonServiceImpl
    - (t_Season)currentSeason
    {
        // Get Date
        int month = [self currentMonth];
        // Get Latitude
        float latitude = [self currentLatitude];
        // work out the season based on month and latitude 
        // (i.e, Northern or Southern hemisphere)
        return season;
    }
}

我可以通过使用一个类别来公开currentMonthcurrentLatitude方法来测试上面的公共方法,然后使用OCMock来进行部分mock。这至少让我可以测试我的代码实现,以确定基于日期和地点的季节。

但是

1) 对我来说,使用类别扩展来有效地将两个私有方法转换为公共方法似乎是一种代码气味。2) 这并不是在测试我是否正确地获取了位置。

那么,我该如何对其进行编码,以便测试我是否正确获取了当前位置呢?我的意思是,使用不同的指定init方法来接受CLLocationManager的实例是最好的方法。当代码在生产中运行时,这可能是一个模拟的实例,也可能是真实的实例?此外,currentSeason方法是否应该更改为类似currentSeasonForDate:NSDate的方法?

这类问题的传统解决方案是依赖注入,它有助于推动关注点分离。SeasonService的工作是根据纬度和月份来确定季节。确定/存储纬度和月份可能应该由其他类负责。

根据系统的要求,这可以通过构造参数或方法参数来实现。无论哪种方式,都可以让你直接模拟出一个特定的月份或纬度,而不是诉诸于分类技巧。

构造函数注入:

- initWithLatitudeProvider:id<LatitudeProvider> latitudeProvider
          andMonthProvider:id<MonthProvider> monthProvider { 
    // set ivars or properties
}
- (t_Season)currentSeason
{
    // Get Date
    int month = [[self monthProvider] currentMonth];
    // Get Latitude
    float latitude = [[self latitudeProvider] currentLatitude];
    // work out the season based on month and latitude 
    // (i.e, Northern or Southern hemisphere)
    return season;
}

或方法参数注入:

- (t_Season)currentSeasonAtLatitude:(float)latitude inMonth:(int)month
{
    // work out the season based on month and latitude 
    // (i.e, Northern or Southern hemisphere)
    return season;
}

最新更新