Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a simple data object class which implements NSCoding?

I have a small data object that needs to be serialized and deserialized. Lets say it is called WeatherDetails, and it looks like this:

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"];
}

Currently my tests look like this;

#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]);
}

I have a gut feeling that this approach to testing if all properties are correctly encoded is not really optimal as there is duplication, but I can't really think of a better approach. Does anyone have a tip for me on improving this?

like image 206
MrJre Avatar asked Oct 18 '25 14:10

MrJre


1 Answers

What you really want to test is that the object is same before and after you archive it.

Implement a method in your WeatherDetails to compare the objects (or override isEqual:).

- (BOOL)isEqualToWeatherDetails:(WeatherDetails *)details
{
    if (![details isKindOfClass:[WeatherDetails class]]) return NO;
    return [self.weatherCode == details.weatherCode && self.weatherDescription isEqualToString:details.weatherDescription];
}

Then you can do all your equality comparisons at once:

- (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]);
}

If you overrode isEqual: then you could compare doing this:

XCTAssertEqualObjects(details, unarchive);

Apple tends to add additional methods (isEqualToArray:, isEqualToDictionary:). isEqual: is used by collections like NSSet and NSDictionary

like image 132
Kevin Avatar answered Oct 21 '25 04:10

Kevin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!