如何测试实现NSCoding的简单数据对象类?

时间:2015-08-20 14:51:03

标签: ios unit-testing xctest

我有一个需要序列化和反序列化的小型数据对象。可以说它被称为WeatherDetails,它看起来像这样:

WeatherDetails.h

@interface WeatherDetails : NSObject <NSCoding>
{
@private

@protected
}

#pragma mark - Properties

@property (nonatomic, copy) NSString *weatherCode;
@property (nonatomic, copy) NSString *weatherDescription;

@end

WeatherDetails.m

#import "WeatherDetails.h"

@implementation WeatherDetails

NSString *const WEATHER_DETAILS_WEATHER_CODE_KEY = @"s";
NSString *const WEATHER_DETAILS_WEATHER_DESCRIPTION_KEY = @"sT";

#pragma mark - Initialization, NSCoding and Dealloc

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];

    _weatherCode = [aDecoder decodeObjectForKey:@"weatherCode"];
    _weatherDescription = [aDecoder decodeObjectForKey:@"weatherDescription"];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_weatherCode forKey:@"weatherCode"];
    [aCoder encodeObject:_weatherDescription forKey:@"weatherDescription"];
}

目前我的测试看起来像这样;

#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import "WeatherDetails.h"
@interface WeatherDetailsTests : XCTestCase

@end

@implementation WeatherDetailsTests

- (void)testThatWeatherCodeIsEncoded
{
    WeatherDetails *details = [[WeatherDetails alloc] init];
    [details setWeatherCode:@"A"];

    NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:details];

    WeatherDetails *unarchive = [NSKeyedUnarchiver unarchiveObjectWithData:archive];

    XCTAssertEqualObjects(@"A", [unarchive weatherCode]);
}

- (void)testThatWeatherDescriptionIsEncoded
{
    WeatherDetails *details = [[WeatherDetails alloc] init];
    [details setWeatherDescription:@"A"];

    NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:details];

    WeatherDetails *unarchive = [NSKeyedUnarchiver unarchiveObjectWithData:archive];

    XCTAssertEqualObjects(@"A", [unarchive weatherDescription]);
}

我有一种直觉,即如果所有属性都被正确编码,这种测试方法并不是最佳的,因为存在重复,但我无法想到更好的方法。有没有人对我提出改进的建议?

2 个答案:

答案 0 :(得分:3)

您真正想要测试的是在归档之前和之后对象是相同的。

isEqual:中实施一种方法来比较对象(或覆盖- (BOOL)isEqualToWeatherDetails:(WeatherDetails *)details { if (![details isKindOfClass:[WeatherDetails class]]) return NO; return [self.weatherCode == details.weatherCode && self.weatherDescription isEqualToString:details.weatherDescription]; } )。

- (void)testNSCoder
{
    WeatherDetails *details = [[WeatherDetails alloc] init];
    [details setWeatherCode:@"A"];
    details.weatherDescription = @"Cloudy";

    NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:details];

    WeatherDetails *unarchive = [NSKeyedUnarchiver unarchiveObjectWithData:archive];

    XCTAssertTrue([details isEqualToWeatherDetails:unarchive]);
}

然后你可以一次完成所有的平等比较:

isEqual:

如果你覆盖XCTAssertEqualObjects(details, unarchive); ,那么你可以比较这样做:

isEqualToArray:

Apple倾向于添加其他方法(isEqualToDictionary:isEqual:)。 Title由NSSet和NSDictionary等集合使用

答案 1 :(得分:2)

最后,我通过应用custom assertion;

对其进行了改进
  

何时使用

     

只要满足以下任何条件,我们就应该考虑创建自定义断言:

     
      
  • 我们发现自己在测试后的测试中编写(或克隆)相同的断言逻辑
  •   
  • 我们发现自己在测试的结果验证部分编写了条件测试逻辑。也就是说,我们对Assertion方法的调用嵌入在if语句或循环中。
  •   
  • 我们测试的结果验证部分遭受了模糊测试,因为我们在测试中使用了程序而不是声明性结果验证。
  •   
  • 当断言失败时,我们发现自己正在进行频繁调试,因为它们没有提供足够的信息。
  •   

自定义断言目前看起来像这样:

/*!
 * @define XCTAssertEqualSerialized(value, object, selector)
 * Serializes (\a object), then desirializes it and compares result's property (\a propertyName) to (\a value) with XCTAssertEqualObjects
 * @param value Value to compare.
 * @param object Object to serialize.
 * @param selector Objects property to compare.
 */

#define XCTAssertEqualSerialized(value, object, selector) \
({ \
NSData *archived = [NSKeyedArchiver archivedDataWithRootObject:object]; \
NSObject *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:archived]; \
XCTAssertEqualObjects(value, [unarchived valueForKeyPath:NSStringFromSelector(selector)]); \
})

/*!
 * @define XCTAssertNotNilSerialized(object, selector)
 * Serializes (\a object), then desirializes it and checks result's property (\a propertyName) is not nil with XCTAssertNotNil
 * @param object Object to serialize.
 * @param selector Objects property to compare.
 */

#define XCTAssertNotNilSerialized(object, selector) \
({ \
NSData *archived = [NSKeyedArchiver archivedDataWithRootObject:object]; \
NSObject *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:archived]; \
XCTAssertNotNil([unarchived valueForKeyPath:NSStringFromSelector(selector)]); \
})

这导致测试看起来像这样:

- (void)testThatWeatherCodeIsEncoded
{
    WeatherDetails *details = [[WeatherDetails alloc] init];
    [details setWeatherCode:@"A"];

    XCTAssertEqualSerialized(@"A", details, @selector(weatherCode));
}

- (void)testThatWeatherDescriptionIsEncoded
{
    WeatherDetails *details = [[WeatherDetails alloc] init];
    [details setWeatherDescription:@"A"];

    XCTAssertEqualSerialized(@"A", details, @selector(weatherDescription));
}