在运行时更改iOS模拟器的当前区域设置

时间:2013-10-02 18:11:28

标签: ios objective-c cocoa-touch localization internationalization

在开发一组用于将数值和日期转换为字符串的日期计算和语言规则时,我正在编写断言字符串格式化方法结果的测试。对它的想象断言可能如下所示:

NSAssert([dateString isEqualToString:@"Three days, until 6:00 PM"], @"Date string should match expectation");

但是,由于应用程序已针对多种语言进行本地化,并且我的同事开发人员也来自不同的语言环境,因此您的设备或模拟器可能会设置为与测试所针对的语言环境不同的语言环境。为...写的。在这种情况下,dateString的内容可能类似于:

@"Drie dagen, tot 18:00" // the assertion fails
@"Drei Tage, bis 18 Uhr" // the assertion also fails

这可能是也可能不是这些语言环境的正确日期符号,但我的问题所在的部分是,当底层代码使用Apple API时,如何能够运行测试到特定的语言环境:

[NSDateFormatter localizedStringFromDate:date 
                 dateStyle:NSDateFormatterNoStyle 
                 timeStyle:NSDateFormatterShortStyle];

我希望在我的断言中用两种或更多种语言覆盖,例如:

[NSSomething actionToSetTheLocaleTo:@"en_US"];
dateString = ...; // the formatting
NSAssert([dateString isEqualToString:@"Three days, until 6:00 PM"], @"match en_US");

[NSSomething actionToSetTheLocaleTo:@"nl_NL"];
dateString = ...; // the formatting
NSAssert([dateString isEqualToString:@"Drie dagen, tot 18:00"], @"match nl_NL");

谁知道如何实现这种效果?

注意:

  • 更改首选语言不会削弱它,还需要影响NSDateFormatter和NSNumberFormatter行为。
  • 因为这仅用于单元测试目的,所以我会满足于使用私有API。但是,为了其他人在这篇文章上磕磕绊绊的好处,首选公共API。
  • 将自定义区域设置传递给每个日期或数字格式API可能是最后一个考虑因素,但我发布此问题希望 避免 回归到那些极端措施。如果您知道这是唯一的解决方案,请提供一些参考,我将不再浪费时间

有关该主题的链接:

3 个答案:

答案 0 :(得分:19)

@Desmond指出了一个有效的解决方案。在他将答案放在这里之前,让我总结一下我最后用一些代码做的事情。

事实证明,解决方案与类方法在内部使用的swizzling the methods“一样简单”:

beforeEach(^{
    [NSBundle ttt_overrideLanguage:@"nl"];
    [NSLocale ttt_overrideRuntimeLocale:[NSLocale localeWithLocaleIdentifier:@"nl_NL"]];
});

afterEach(^{
    [NSLocale ttt_resetRuntimeLocale];
    [NSBundle ttt_resetLanguage];
});

您在上面看到的ttt_...方法使用NSObject,NSLocale和NSBundle上的类别来检查它是否应该使用原始方法或返回其他内容。这种方法在编写测试时可以完美运行,虽然它在技术上并不使用任何私有API,但我强烈建议您只在测试设置中使用它,而不是在提交给App Store进行审查的任何内容。

In this gist you'll find the Objective-C categories我已添加到应用的测试目标以达到所需的行为。

答案 1 :(得分:2)

由于这是单元测试,您是否在NSDateFormatter上尝试了setting the locale?您不必在整个模拟器上设置区域设置,您可以将其作为测试的参数。大多数依赖于语言环境的可可方法都可以将其作为参数或属性。

类似的东西:

NSLocale *locale = [NSLocale localeWithLocaleIdentifier:@"en-US"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:locale];
[formatter setDateStyle: NSDateFormatterNoStyle];
[formatter setTimeStyle: NSDateFormatterShortStyle];
[formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
thing = [formatter stringForObjectValue:date];

您使用的localizedStringFromDate:dateStyle:timeStyle:方法是documented here,除了语言环境之外,它非常明确。因此,您可以执行相同的步骤,但使用上述步骤将语言环境设置为系统当前语言环境以外的其他语言。

答案 2 :(得分:2)

我看到的唯一方法就是提到了什么。但是,你提到它存在于很多地方。

不要重写所有当前代码,而是在 pch 中做一些像

这样的事情。
#import "UnitTestDateFormatter.h"
#define NSDateFormatter UnitTestDateFormatter

然后只需创建一个子类来处理它:

@implementation UnitTestDateFormatter

- (id) init
{
    self = [super init];

    if(self != nil)
    {
        [self setLocale:...];
    }

    return self;
}

@end

至少那时你的代码可以保持不变。