如何使用 RestKit 正确测试映射(嵌套映射,对多)
Posted
技术标签:
【中文标题】如何使用 RestKit 正确测试映射(嵌套映射,对多)【英文标题】:How to correctly test mapping with RestKit (nested mapping, to-many) 【发布时间】:2013-12-05 20:48:51 【问题描述】:我正在尝试测试我的核心数据模型的响应映射。我在 Core Data 模型中有几个类,但其中 2 个我很想测试。第一类是CTFUser
类,保存用户的基本信息,第二类是CTFCharacter
类。用户可能有几个角色(某种玩家)多对多关系。我想做的是为CTFUser
类和CTFUser
和CTFCharacter
类之间的关系编写正确的测试。当我编写没有属性characters
的测试时,NSOrderedSet
类型测试通过了。当我尝试测试属性characters
而不检查value
的RKPropertyMappingTestExpectation
测试通过。但是当我想检查这个映射的值时,我遇到了崩溃。
测试中使用的 JSON 夹具:
"username": "tomkowz12",
"email": "tmk.szlc@gmail.com",
"password": "password123",
"nick": "black_lord",
"location": [10, 20],
"characters" : [
"type": 1,
"total_time": 21,
"total_score": 100,
"health": 100,
"level": 20,
"is_active": 1
,
"type": 2,
"total_time": 23,
"total_score": 98,
"health": 55,
"level": 12,
"is_active": 0
]
CTFUser 类
@interface CTFUser : CustomManagedObject
@property (nonatomic, retain) NSString * email;
@property (nonatomic, retain) NSString * nick;
@property (nonatomic, retain) NSString * username;
@property (nonatomic, retain) NSString * password;
@property (nonatomic, retain) id location;
@property (nonatomic, retain) CTFGame *game;
@property (nonatomic, retain) NSOrderedSet *characters;
+ (NSDictionary *)dictionaryForResponseMapping;
@end
和具有此类属性映射的字典字典:
+ (NSDictionary *)dictionaryForResponseMapping
return @@"username" : @"username",
@"email" : @"email",
@"password" : @"password",
@"nick" : @"nick",
@"location" : @"location";
CTFCharacter 类
@interface CTFCharacter : CustomManagedObject
@property (nonatomic, retain) NSNumber * type;
@property (nonatomic, retain) NSNumber * totalTime;
@property (nonatomic, retain) NSNumber * totalScore;
@property (nonatomic, retain) NSNumber * health;
@property (nonatomic, retain) NSNumber * level;
@property (nonatomic, retain) NSNumber * active;
@property (nonatomic, retain) CTFUser *user;
+ (NSDictionary *)dictionaryResponseMapping;
@end
和带有属性映射的字典:
+ (NSDictionary *)dictionaryResponseMapping
return @@"type": @"type",
@"total_time": @"totalTime",
@"total_score": @"totalScore",
@"health": @"health",
@"level": @"level",
@"is_active": @"active";
** 单元测试 ** - (void)testUserResponseMApping id parsedJSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"user-response.json"];
/// Configure mapping
RKEntityMapping *userMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([CTFUser class]) inManagedObjectStore:_manager.managedObjectStore];
[userMapping addAttributeMappingsFromDictionary:[CTFUser dictionaryForResponseMapping]];
RKEntityMapping *characterMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectStore:_manager.managedObjectStore];
[characterMapping addAttributeMappingsFromDictionary:[CTFCharacter dictionaryResponseMapping]];
[userMapping addRelationshipMappingWithSourceKeyPath:@"characters" mapping:characterMapping];
/// Configure expectations
RKMappingTest *test = [RKMappingTest testForMapping:userMapping sourceObject:parsedJSON destinationObject:nil];
test.managedObjectContext = _service.managedObjectContext;
RKPropertyMappingTestExpectation *usernameExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"username" destinationKeyPath:@"username" value:@"tomkowz12"];
[test addExpectation:usernameExpectation];
RKPropertyMappingTestExpectation *emailExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"email" destinationKeyPath:@"email" value:@"tmk.szlc@gmail.com"];
[test addExpectation:emailExpectation];
RKPropertyMappingTestExpectation *passwordExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"password" destinationKeyPath:@"password" value:@"password123"];
[test addExpectation:passwordExpectation];
RKPropertyMappingTestExpectation *nickExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"nick" destinationKeyPath:@"nick" value:@"black_lord"];
[test addExpectation:nickExpectation];
RKPropertyMappingTestExpectation *locationExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"location" destinationKeyPath:@"location" value:@[@(10), @(20)]];
[test addExpectation:locationExpectation];
/// Configure expectation objects
CTFCharacter *firstCharacter = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectContext:_service.managedObjectContext];
firstCharacter.type = @(1);
firstCharacter.totalTime = @(21);
firstCharacter.totalScore = @(100);
firstCharacter.health = @(100);
firstCharacter.level = @(20);
firstCharacter.active = @YES;
CTFCharacter *secondCharacter = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectContext:_service.managedObjectContext];
secondCharacter.type = @(2);
secondCharacter.totalTime = @(23);
secondCharacter.totalScore = @(98);
secondCharacter.health = @(55);
secondCharacter.level = @(12);
secondCharacter.active = @NO;
NSOrderedSet *set = [[NSOrderedSet alloc] initWithArray:@[firstCharacter, secondCharacter]];
RKPropertyMappingTestExpectation *charactersExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"characters" destinationKeyPath:@"characters" value:set]; /// <-- this won't pass
// [RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"characters" destinationKeyPath:@"characters"]; /// <-- this pass
[test addExpectation:charactersExpectation];
[test verify];
崩溃日志
<unknown>:0: error: -[CTFUserTests testUserResponseMApping] : mapped to unexpected _NSFaultingMutableOrderedSet value 'Relationship 'characters' on managed object (0x8c4fba0) <CTFUser: 0x8c4fba0> (entity: CTFUser; id: 0x8c4d2a0 <x-coredata:///CTFUser/t28A63306-B2D2-41F5-A0C3-1BE85520B04710> ; data:
characters = (
"0x8c52470 <x-coredata:///CTFCharacter/t28A63306-B2D2-41F5-A0C3-1BE85520B04711>",
"0x8c4d330 <x-coredata:///CTFCharacter/t28A63306-B2D2-41F5-A0C3-1BE85520B04712>"
);
email = "tmk.szlc@gmail.com";
game = nil;
location = "(\n 10,\n 20\n)";
nick = "black_lord";
password = password123;
username = tomkowz12;
) with objects (
<CTFCharacter: 0x8c528f0> (entity: CTFCharacter; id: 0x8c52470 <x-coredata:///CTFCharacter/t28A63306-B2D2-41F5-A0C3-1BE85520B04711> ; data:
active = 1;
health = 100;
level = 20;
totalScore = 100;
totalTime = 21;
type = 1;
user = "0x8c4d2a0 <x-coredata:///CTFUser/t28A63306-B2D2-41F5-A0C3-1BE85520B04710>";
),
<CTFCharacter: 0x8c4ef60> (entity: CTFCharacter; id: 0x8c4d330 <x-coredata:///CTFCharacter/t28A63306-B2D2-41F5-A0C3-1BE85520B04712> ; data:
active = 0;
health = 55;
level = 12;
totalScore = 98;
totalTime = 23;
type = 2;
user = "0x8c4d2a0 <x-coredata:///CTFUser/t28A63306-B2D2-41F5-A0C3-1BE85520B04710>";
)
)'
(
0 CoreFoundation 0x021185e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x01e9b8b6 objc_exception_throw + 44
2 CoreFoundation 0x021a86a1 -[NSException raise] + 17
3 Capture The FlagTests 0x0a0f1d60 -[RKMappingTest verifyExpectation:] + 464
4 Capture The FlagTests 0x0a0f2054 -[RKMappingTest verify] + 404
5 Capture The FlagTests 0x0a002cf0 -[CTFUserTests testUserResponseMApping] + 3648
6 CoreFoundation 0x0210cd1d __invoking___ + 29
7 CoreFoundation 0x0210cc2a -[NSInvocation invoke] + 362
8 XCTest 0x201032bf -[XCTestCase invokeTest] + 212
9 XCTest 0x2010338d -[XCTestCase performTest:] + 111
10 XCTest 0x2010417c -[XCTest run] + 82
11 XCTest 0x20102a44 -[XCTestSuite performTest:] + 139
12 XCTest 0x2010417c -[XCTest run] + 82
13 XCTest 0x20102a44 -[XCTestSuite performTest:] + 139
14 XCTest 0x2010417c -[XCTest run] + 82
15 XCTest 0x20102a44 -[XCTestSuite performTest:] + 139
16 XCTest 0x2010417c -[XCTest run] + 82
17 XCTest 0x20105aa1 +[XCTestProbe runTests:] + 183
18 Foundation 0x01ad212c __NSFireDelayedPerform + 372
19 CoreFoundation 0x020d6bd6 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
20 CoreFoundation 0x020d65bd __CFRunLoopDoTimer + 1181
21 CoreFoundation 0x020be628 __CFRunLoopRun + 1816
22 CoreFoundation 0x020bdac3 CFRunLoopRunSpecific + 467
23 CoreFoundation 0x020bd8db CFRunLoopRunInMode + 123
24 GraphicsServices 0x03ade9e2 GSEventRunModal + 192
25 GraphicsServices 0x03ade809 GSEventRun + 104
26 UIKit 0x00c09d3b UIApplicationMain + 1225
27 Capture The Flag 0x00008d0d main + 141
28 libdyld.dylib 0x0275670d start + 1
)
我在哪里做错了?应该如何测试characters
属性的值?其他值正在测试中并通过。
编辑:
当我发出请求时,服务器的响应看起来不错,所有属性和关系都映射正确,但测试问题仍然存在。
- (void)getUser
CoreDataService *_service = [CoreDataService sharedInstance];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:_service.managedObjectModel];
_connection.manager.managedObjectStore = managedObjectStore;
RKEntityMapping *userMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([CTFUser class]) inManagedObjectStore:managedObjectStore];
[userMapping addAttributeMappingsFromDictionary:[CTFUser dictionaryForResponseMapping]];
RKEntityMapping *characterMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectStore:managedObjectStore];
[characterMapping addAttributeMappingsFromDictionary:[CTFCharacter dictionaryResponseMapping]];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"characters" toKeyPath:@"characters" withMapping:characterMapping]];
RKResponseDescriptor *descriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping method:RKRequestMethodGET pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[_connection.manager addResponseDescriptor:descriptor];
[_connection.manager getObject:nil path:@"test" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
CTFUser *fetchedUser = (CTFUser *)mappingResult.firstObject;
NSLog(@"%@", fetchedUser);
failure:^(RKObjectRequestOperation *operation, NSError *error)
NSLog(@"failure");
];
提前谢谢你。
【问题讨论】:
【参考方案1】:好的,我做到了。解决方法很简单。昨天很晚,所以我没有看 RestKit 文档。
而不是使用:
expectationWithSourceKeyPath:destinationKeyPath:value:
我用过:
expectationWithSourceKeyPath:destinationKeyPath:evaluationBlock:
修改后的代码如下:
RKPropertyMappingTestExpectation *charactersExpectation =
[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"characters" destinationKeyPath:@"characters" evaluationBlock:^BOOL(RKPropertyMappingTestExpectation *expectation, RKPropertyMapping *mapping, id mappedValue, NSError *__autoreleasing *error)
NSArray *commitedKeysToCompare = @[@"type, totalTime, totalScore, healt, level, active"];
NSArray *array = [mappedValue allObjects];
BOOL containsTwoItems = array.count == 2;
/// Test First character
CTFCharacter *firstFetchedCharacter = (CTFCharacter *)array[0];
CTFCharacter *firstReferenceCharacter = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectContext:_service.managedObjectContext];
firstReferenceCharacter.type = @(1);
firstReferenceCharacter.totalTime = @(21);
firstReferenceCharacter.totalScore = @(100);
firstReferenceCharacter.health = @(100);
firstReferenceCharacter.level = @(20);
firstReferenceCharacter.active = @YES;
BOOL isFirstEqual = [[firstFetchedCharacter committedValuesForKeys:commitedKeysToCompare] isEqual:[firstReferenceCharacter committedValuesForKeys:commitedKeysToCompare]];
/// Test Second character
CTFCharacter *secondFetchedCharacter = (CTFCharacter *)array[1];
CTFCharacter *secondReferenceCharacter = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([CTFCharacter class]) inManagedObjectContext:_service.managedObjectContext];
secondReferenceCharacter.type = @(2);
secondReferenceCharacter.totalTime = @(23);
secondReferenceCharacter.totalScore = @(98);
secondReferenceCharacter.health = @(55);
secondReferenceCharacter.level = @(12);
secondReferenceCharacter.active = @NO;
BOOL isSecondEqual = [[secondFetchedCharacter committedValuesForKeys:commitedKeysToCompare] isEqual:[secondReferenceCharacter committedValuesForKeys:commitedKeysToCompare]];
return isFirstEqual && isSecondEqual && containsTwoItems;
];
【讨论】:
以上是关于如何使用 RestKit 正确测试映射(嵌套映射,对多)的主要内容,如果未能解决你的问题,请参考以下文章