简体   繁体   English

避免使用NSDateFormatter进行脆弱的单元测试

[英]Avoiding fragile unit tests with NSDateFormatter

What is the best practice for testing NSDateFormatter methods? 测试NSDateFormatter方法的最佳实践是什么? For example, lets say I have a method: 例如,假设我有一个方法:

- (NSString *)formatStringFromDate:(NSDate *)date {
    NSDateFormatter *f = [[NSDateFormatter alloc] init];
    [f setTimeStyle:NSDateFormatterShortStyle];
    [f setDateStyle:NSDateFormatterNoStyle];

    return [f stringFromDate:date];
}

There are two ways I can think of testing this method using Kiwi: 我可以考虑使用Kiwi测试这种方法的两种方法:

1) Create the same formatter in the unit test: 1)在单元测试中创建相同的格式化程序:

it(@"should format a date", ^{
    NSDate *date = [NSDate date];
    NSDateFormatter *f = [[NSDateFormatter alloc] init];
    [f setTimeStyle:NSDateFormatterShortStyle];
    [f setDateStyle:NSDateFormatterNoStyle];

    [[[testObject formatStringFromDate:date] should] equal:[f stringFromDate:date]];
});

2) Explicitly write the intended output: 2)明确写出预期的输出:

it(@"should format a date", ^{
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:1385546122];
    NSDateFormatter *f = [[NSDateFormatter alloc] init];
    [f setTimeStyle:NSDateFormatterShortStyle];
    [f setDateStyle:NSDateFormatterNoStyle];

    [[[testObject formatStringFromDate:date] should] equal:@"9:55 am"];
});

Now, to me, #1 seems a bit redundant. 现在,对我来说,#1似乎有点多余。 I know the test will pass as I'm essentially duplicating the method in my unit test. 我知道测试将通过,因为我实质上是在单元测试中复制方法。

Method #2 is a non-starter, as it is incredibly fragile. 方法2是非入门类,因为它非常脆弱。 It completely relies on the test devices current locale being what you'd expect. 它完全依赖于您期望的测试设备的当前语言环境。

So my question is: is there a more appropriate method to test this method, or should I just go ahead with test method #1. 所以我的问题是:是否有一种更合适的方法来测试此方法,还是我应该继续进行测试方法1。

As you say in your comment, what you want to test is that the cell's detailTextLabel's text is set to date with the expected format when a date is present. 正如您在评论中所说,要测试的是,当存在日期时,单元格的detailTextLabel的文本设置为具有预期格式的日期。

This can be tested in several different ways, I expose here the one I would go for. 可以用几种不同的方法来测试这一点,在这里我将介绍我想要的一种。

First of all, creating a date formatter each time we want to format a date is not efficient and it makes it more difficult to test. 首先,每次我们要格式化日期时都创建日期格式化程序效率不高,这使测试变得更加困难。

So what I suggest is to create a date formatter property in your table view controller: 所以我建议在表视图控制器中创建一个日期格式器属性:

// .h
@property (strong, nonatomic) NSDateFormatter *dateFormatter;

// .m
- (NSDateFormatter *) dateFormatter
{
    if (_dateFormatter == nil)
    {
        _dateFormatter = [[NSDateFormatter alloc] init];
        [_dateFormatter setTimeStyle:NSDateFormatterShortStyle];
        [_dateFormatter setDateStyle:NSDateFormatterNoStyle];

    }
    return _dateFormatter;
}

For the date formatter I would create a simple test that verifies the timeStyle and dateStyle. 对于日期格式化程序,我将创建一个简单的测试来验证timeStyle和dateStyle。 I am not familiar with Kiwi so I use OCUnit assertions: 我不熟悉Kiwi,所以我使用OCUnit断言:

TableViewController *sut;// Instantiate the table view controller
STAssertTrue(sut.dateFormatter.timeStyle == NSDateFormatterShortStyle, nil);
STAssertTrue(sut.dateFormatter.dateStyle == setDateStyle:NSDateFormatterNoStyle, nil);

After having this, the idea is to create a test that verifies that the cell returned by tableView:cellForRowAtIndexPath: has its detailTextLabel's text set with the string returned by the formatter. 完成此操作后,我们的想法是创建一个测试,以验证tableView:cellForRowAtIndexPath:返回的单元格是否设置了detailTextLabel的文本,并设置了格式化程序返回的字符串。 As you said testing the string returned by the date formatter is fragile so we can mock it, stub stringFromDate: returning a constant and verify that the detailTextLabel's text is set to that constant. 正如您所说的那样,测试日期格式化程序返回的字符串很脆弱,因此我们可以对它进行模拟,存根stringFromDate:返回一个常量,并验证detailTextLabel的文本是否设置为该常量。

So we write the test. 因此,我们编写了测试。 I'd try to write it with Kiwi mocks, so sorry if I do something wrong - the idea is the important thing: 我会尝试用Kiwi模拟程序编写它,所以如果我做错了事,抱歉-这个想法很重要:

TableViewController *sut;// Instantiate the table view controller

id mockDateFormatter = [NSDateFormatter mock];

NSString * const kFormattedDate = @"formattedDate";
NSDate * const date = [NSDate date];

[mockDateFormatter stub:@selector(stringFromDate:) andReturn:kFormattedDate withArguments:date,nil];

sut.dateFormatter = mockDateFormatter;
sut.dates = @[date];// As an example we have an array of dates to show. In the real case we would have an array of the objects you want to show in the table view.

[sut view];// In case we have the registered cells...
UITableViewCell *cell = [sut tableView:sut.tableView 
                 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

STAssertEqualObjects(cell.detailTextLabel.text, kFormattedDate, nil);

And the method to satisfy that test would be something like this: 满足该测试要求的方法如下所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    // I assume you have a standard cell registered in your table view
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier"];

    NSDate *date = [self.dates objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = [self.dateFormatter stringFromDate:date];

    return cell;
}

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM