Updated mapping test API for 0.20

* Reimplemented connection tests to work with `RKConnectionDescription`. Introduced new `RKConnectionTestExpectation` class
* Slimmed down API by removing proxy methods on `RKMappingTest`
* Eliminated `verifiesOnExpect` behavior in favor of using `evaluateExpectation:`
* Ensured compatibility with RKKiwiMatchers
This commit is contained in:
Blake Watters
2012-12-09 12:24:30 -05:00
parent f3cd0f6e07
commit 4c735bafdd
7 changed files with 370 additions and 231 deletions

View File

@@ -0,0 +1,83 @@
//
// RKConnectionTestExpectation.h
// RestKit
//
// Created by Blake Watters on 12/8/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
/**
An `RKConnectionTestExpectation` object defines an expectation that a Core Data relationship is connected during the execution of a `RKMappingTest`. These expectation are used to unit test a connection specified via an `RKConnectionDescription` object.
@see `RKMappingTest`
@see `RKConnectionDescription`
*/
@interface RKConnectionTestExpectation : NSObject
///----------------------------
/// @name Creating Expectations
///----------------------------
/**
Creates and returns a connection expectation for the specified relationship name, attributes dictionary, and value.
@param relationshipName The name of the relationship expected to be connected.
@param attributes A dictionary specifying the attributes that are expected to be used to establish the connection.
@param value The value that is expected to be set for the relationship when the connection is established.
@return A newly constructed connection expectation, initialized with the given relationship name, attributes dictionary, and expected value.
*/
+ (id)expectationWithRelationshipName:(NSString *)relationshipName attributes:(NSDictionary *)attributes value:(id)value;
/**
Initializes the receiver with the given relationship name, attributes dictionary, and value.
@param relationshipName The name of the relationship expected to be connected.
@param attributes A dictionary specifying the attributes that are expected to be used to establish the connection.
@param value The value that is expected to be set for the relationship when the connection is established.
@return The receiver, initialized with the given relationship name, attributes dictionary, and expected value.
*/
- (id)initWithRelationshipName:(NSString *)relationshipName attributes:(NSDictionary *)attributes value:(id)value;
///------------------------------------
/// @name Accessing Expectation Details
///------------------------------------
/**
The name of the relationship that is expected to be connected. Cannot be `nil`.
*/
@property (nonatomic, copy, readonly) NSString *relationshipName;
/**
The dictionary of attributes that are expected to be used when the connection is established. May be `nil`.
*/
@property (nonatomic, copy, readonly) NSDictionary *attributes;
/**
The value that is expected to be set for the relationship when connected. May be `nil`.
A value of `nil` indicates that expectation does not specify an exact value for the connection, only that it was set during the execution of the test. A value of `[NSNull null]` indicates that the connection is expected to be connected to a nil value.
*/
@property (nonatomic, strong, readonly) id value;
/**
Returns a string summary of the connection that is expected to be established.
@return A string describing the expected connection.
*/
- (NSString *)summary;
@end

View File

@@ -0,0 +1,66 @@
//
// RKConnectionTestExpectation.m
// RestKit
//
// Created by Blake Watters on 12/8/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <CoreData/CoreData.h>
#import "RKConnectionTestExpectation.h"
#import "RKObjectUtilities.h"
@interface RKConnectionTestExpectation ()
@property (nonatomic, copy, readwrite) NSString *relationshipName;
@property (nonatomic, copy, readwrite) NSDictionary *attributes;
@property (nonatomic, strong, readwrite) id value;
@end
@implementation RKConnectionTestExpectation
+ (id)expectationWithRelationshipName:(NSString *)relationshipName attributes:(NSDictionary *)attributes value:(id)value
{
return [[self alloc] initWithRelationshipName:relationshipName attributes:attributes value:value];
}
- (id)initWithRelationshipName:(NSString *)relationshipName attributes:(NSDictionary *)attributes value:(id)value
{
NSParameterAssert(relationshipName);
NSAssert(value == nil ||
[value isKindOfClass:[NSManagedObject class]] ||
RKObjectIsCollectionContainingOnlyManagedObjects(value), @"Can only expect a connection to `nil`, a `NSManagedObject`, or a collection of `NSManagedObject` objects");
self = [self init];
if (self) {
self.relationshipName = relationshipName;
self.attributes = attributes;
self.value = value;
}
return self;
}
- (NSString *)summary
{
return [NSString stringWithFormat:@"connect relationship '%@'", self.relationshipName];
}
- (NSString *)description
{
NSMutableString *description = [[self summary] mutableCopy];
if (self.attributes) [description appendFormat:@" using attributes %@", self.attributes];
if (self.value) [description appendFormat:@" to value %@", self.value];
return description;
}
@end

View File

@@ -21,7 +21,7 @@
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "RKMappingOperation.h"
#import "RKMappingTestExpectation.h"
#import "RKPropertyMappingTestExpectation.h"
@protocol RKMappingOperationDataSource, RKManagedObjectCaching;
@@ -45,10 +45,28 @@ extern NSString * const RKMappingTestVerificationFailureException;
enum {
RKMappingTestUnsatisfiedExpectationError, // An expected mapping event did not occur
RKMappingTestEvaluationBlockError, // An evaluation block returned `NO` when evaluating a mapping event
RKMappingTestValueInequalityError, // A mapped value was not equal to the expected value
RKMappingTestValueInequalityError, // A value was not equal to the expected value
RKMappingTestMappingMismatchError, // A mapping occurred using an unexpected `RKObjectMapping` object
};
/**
@define RKMappingTestExpectationTestCondition
@abstract Tests a condition and returns `NO` and error if it is not true.
@discussion This is a useful macro when constructing mapping test evaluation blocks. It will test a condition and return `NO` as well as construct an error. This is meant to be used **only** within the body of a `RKMappingTestExpectationEvaluationBlock` object.
@param condition The condition to test.
@param errorCode An error code in the RKMappingTestErrorDomain indicating the nature of the failure.
@param error The NSError object to put the error string into. May be nil, but should usually be the error parameter from the expectation evaluation block.
@param ... A string describing the error.
*/
#define RKMappingTestCondition(condition, errorCode, error, ...) ({ \
if (!(condition)) { \
if (error) { \
*error = [NSError errorWithDomain:RKMappingTestErrorDomain code:errorCode userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:__VA_ARGS__], NSLocalizedDescriptionKey, nil]]; \
} \
return NO; \
} \
})
/**
The `RKMappingTestEvent` object for the mapping event which failed to satify the expectation.
*/
@@ -89,59 +107,29 @@ extern NSString * const RKMappingTestExpectationErrorKey;
*/
- (id)initWithMapping:(RKMapping *)mapping sourceObject:(id)sourceObject destinationObject:(id)destinationObject;
///---------------------------
/// @name Setting Expectations
///---------------------------
/**
Creates and adds an expectation that a key path on the source object will be mapped to a new key path on the destination object.
@param sourceKeyPath A key path on the sourceObject that should be mapped from.
@param destinationKeyPath A key path on the destinationObject that should be mapped to.
@see `RKMappingTestExpectation`
*/
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath;
/**
Creates and adds an expectation that a key path on the source object will be mapped to a new key path on the destination object with a given value.
@param sourceKeyPath A key path on the sourceObject that should be mapped from.
@param destinationKeyPath A key path on the destinationObject that should be mapped from.
@param value A value that is expected to be assigned to destinationKeyPath on the destinationObject.
@see `RKMappingTestExpectation`
*/
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withValue:(id)value;
/**
Creates and adds an expectation that a key path on the source object will be mapped to a new key path on the destination object with a value that passes a given test block.
@param sourceKeyPath A key path on the sourceObject that should be mapped from.
@param destinationKeyPath A key path on the destinationObject that should be mapped to.
@param evaluationBlock A block with which to evaluate the success of the mapping.
@see `RKMappingTestExpectation`
*/
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath passingTest:(RKMappingTestExpectationEvaluationBlock)evaluationBlock;
/**
Creates and adds an expectation that a key path on the source object will be mapped to a new key path on the destination object using the given object mapping.
@param sourceKeyPath A key path on the sourceObject that should be mapped from.
@param destinationKeyPath A key path on the destinationObject that should be mapped to.
@param mapping An object mapping that should be used for mapping the source key path.
@see `RKMappingTestExpectation`
*/
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath usingMapping:(RKMapping *)mapping;
///----------------------------
/// @name Managing Expectations
///----------------------------
/**
Adds an expectation to the receiver to be evaluated during verification.
If the receiver has been configured with `verifiesOnExpect = YES`, the mapping operation is performed immediately and the expectation is evaluated.
@param expectation An expectation object to evaluate during test verification.
@param expectation An expectation object to evaluate during test verification. Must be an instance of `RKPropertyMappingTestExpectation` or `RKConnectionTestExpectation`.
@see `RKMappingTestExpectation`
@see `verifiesOnExpect`
*/
- (void)addExpectation:(RKMappingTestExpectation *)expectation;
- (void)addExpectation:(id)expectation;
/**
Evaluates the given expectation against the mapping test and returns a Boolean value indicating if the expectation is met by the receiver.
Invocation of this method will implicitly invoke `performMapping` if the mapping has not yet been performed.
@param expectation The expectation to evaluate against the receiver. Must be an intance of either `RKPropertyMappingTestExpectation` or `RKConnectionTestExpectation`.
@param error A pointer to an `NSError` object to be set describing the failure in the event that the expectation is not met.
@return `YES` if the expectation is met, else `NO`.
*/
- (BOOL)evaluateExpectation:(id)expectation error:(NSError **)error;
///------------------------
/// @name Verifying Results
@@ -170,17 +158,6 @@ extern NSString * const RKMappingTestExpectationErrorKey;
*/
- (BOOL)evaluate;
/**
Evaluates the given expectation against the mapping test and returns a Boolean value indicating if the expectation is met by the receiver.
Invocation of this method will implicitly invoke `performMapping` if the mapping has not yet been performed.
@param expectation The expectation to evaluate against the receiver.
@param error A pointer to an `NSError` object to be set describing the failure in the event that the expectation is not met.
@return `YES` if the expectation is met, else `NO`.
*/
- (BOOL)evaluateExpectation:(RKMappingTestExpectation *)expectation error:(NSError **)error;
///-------------------------
/// @name Test Configuration
///-------------------------
@@ -218,13 +195,6 @@ extern NSString * const RKMappingTestExpectationErrorKey;
*/
@property (nonatomic, strong, readonly) id destinationObject;
/**
A Boolean value that determines if expectations should be verified immediately when added to the receiver.
**Default**: `NO`
*/
@property (nonatomic, assign) BOOL verifiesOnExpect;
///----------------------------
/// @name Core Data Integration
///----------------------------

View File

@@ -27,6 +27,8 @@
#import "RKLog.h"
// Core Data
#import "RKConnectionDescription.h"
#import "RKConnectionTestExpectation.h"
#import "RKFetchRequestManagedObjectCache.h"
#import "RKManagedObjectMappingOperationDataSource.h"
@@ -122,7 +124,7 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
// Method Definitions for old compilers
- (void)performMapping;
- (void)verifyExpectation:(RKMappingTestExpectation *)expectation;
- (void)verifyExpectation:(RKPropertyMappingTestExpectation *)expectation;
@end
@@ -145,55 +147,43 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
self.mapping = mapping;
self.expectations = [NSMutableArray new];
self.events = [NSMutableArray new];
self.verifiesOnExpect = NO;
self.performedMapping = NO;
}
return self;
}
- (void)addExpectation:(RKMappingTestExpectation *)expectation
- (void)addExpectation:(id)expectation
{
[self.expectations addObject:expectation];
if (self.verifiesOnExpect) {
[self performMapping];
[self verifyExpectation:expectation];
NSParameterAssert(expectation);
if (![expectation isKindOfClass:[RKPropertyMappingTestExpectation class]] && ![expectation isKindOfClass:[RKConnectionTestExpectation class]]) {
[NSException raise:NSInvalidArgumentException
format:@"Invalid expectation: expected an object of type `%@` or `%@`, but instead got a `%@`",
[RKPropertyMappingTestExpectation class], [RKConnectionTestExpectation class], expectation];
}
[self.expectations addObject:expectation];
}
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath
{
[self addExpectation:[RKMappingTestExpectation expectationWithSourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath]];
}
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withValue:(id)value
{
[self addExpectation:[RKMappingTestExpectation expectationWithSourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath value:value]];
}
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath passingTest:(RKMappingTestExpectationEvaluationBlock)evaluationBlock
{
[self addExpectation:[RKMappingTestExpectation expectationWithSourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath evaluationBlock:evaluationBlock]];
}
- (void)expectMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath usingMapping:(RKMapping *)mapping
{
[self addExpectation:[RKMappingTestExpectation expectationWithSourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath mapping:mapping]];
}
- (RKMappingTestEvent *)eventMatchingKeyPathsForExpectation:(RKMappingTestExpectation *)expectation
- (RKMappingTestEvent *)eventMatchingExpectation:(id)expectation
{
for (RKMappingTestEvent *event in [self.events copy]) {
if ([event.sourceKeyPath isEqualToString:expectation.sourceKeyPath] && [event.destinationKeyPath isEqualToString:expectation.destinationKeyPath]) {
return event;
if ([expectation isKindOfClass:[RKPropertyMappingTestExpectation class]]) {
RKPropertyMappingTestExpectation *propertyExpectation = (RKPropertyMappingTestExpectation *) expectation;
if ([event.sourceKeyPath isEqualToString:propertyExpectation.sourceKeyPath] && [event.destinationKeyPath isEqualToString:propertyExpectation.destinationKeyPath]) {
return event;
}
} else if ([expectation isKindOfClass:[RKConnectionTestExpectation class]]) {
RKConnectionTestExpectation *connectionExpectation = (RKConnectionTestExpectation *) expectation;
if ([[event.connection.relationship name] isEqualToString:connectionExpectation.relationshipName]) {
return event;
}
}
}
return nil;
}
- (NSError *)errorForExpectation:(RKMappingTestExpectation *)expectation
- (NSError *)errorForExpectation:(RKPropertyMappingTestExpectation *)expectation
withCode:(NSInteger)errorCode
userInfo:(NSDictionary *)userInfo
description:(NSString *)description
@@ -205,90 +195,123 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
return [NSError errorWithDomain:RKMappingTestErrorDomain code:errorCode userInfo:fullUserInfo];
}
- (BOOL)event:(RKMappingTestEvent *)event satisfiesExpectation:(RKMappingTestExpectation *)expectation error:(NSError **)error
- (BOOL)event:(RKMappingTestEvent *)event satisfiesExpectation:(id)expectation error:(NSError **)error
{
BOOL success;
NSDictionary *userInfo = @{ RKMappingTestEventErrorKey : event,
RKMappingTestExpectationErrorKey : expectation };
if (expectation.evaluationBlock) {
// Let the expectation block evaluate the match
NSError *blockError = nil;
success = expectation.evaluationBlock(expectation, event.propertyMapping, event.value, &blockError);
if (! success) {
if (blockError) {
// If the block has given us an error, use the reason
NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy];
[mutableUserInfo setValue:blockError forKey:NSUnderlyingErrorKey];
NSString *reason = [NSString stringWithFormat:@"expected to %@ with value %@ '%@', but it did not",
expectation, [event.value class], event.value];
*error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:mutableUserInfo
description:[blockError localizedDescription]
reason:reason];
*error = blockError;
} else {
NSString *description = [NSString stringWithFormat:@"evaluation block returned `NO` for %@ value '%@'", [event.value class], event.value];
NSString *reason = [NSString stringWithFormat:@"expected to %@ with value %@ '%@', but it did not",
expectation, [event.value class], event.value];
*error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:userInfo
description:description
reason:reason];
}
}
} else if (expectation.value) {
// Use RestKit comparison magic to match values
success = RKObjectIsEqualToObject(event.value, expectation.value);
if (! success) {
NSString *description = [NSString stringWithFormat:@"mapped to unexpected %@ value '%@'", [event.value class], event.value];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but instead got %@ '%@'",
expectation, [event.value class], event.value];
if (error) *error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:userInfo
description:description
reason:reason];
}
} else if (expectation.mapping) {
if ([event.propertyMapping isKindOfClass:[RKRelationshipMapping class]]) {
// Check the mapping that was used to map the relationship
RKMapping *relationshipMapping = [(RKRelationshipMapping *)event.propertyMapping mapping];
success = [relationshipMapping isEqualToMapping:expectation.mapping];
if ([expectation isKindOfClass:[RKPropertyMappingTestExpectation class]]) {
RKPropertyMappingTestExpectation *propertyExpectation = (RKPropertyMappingTestExpectation *)expectation;
if (propertyExpectation.evaluationBlock) {
// Let the expectation block evaluate the match
NSError *blockError = nil;
success = propertyExpectation.evaluationBlock(expectation, event.propertyMapping, event.value, &blockError);
if (! success) {
NSString *description = [NSString stringWithFormat:@"mapped using unexpected mapping: %@", relationshipMapping];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but was instead mapped using: %@",
expectation, relationshipMapping];
if (blockError) {
// If the block has given us an error, use the reason
NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy];
[mutableUserInfo setValue:blockError forKey:NSUnderlyingErrorKey];
NSString *reason = [NSString stringWithFormat:@"expected to %@ with value %@ '%@', but it did not",
expectation, [event.value class], event.value];
*error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:mutableUserInfo
description:[blockError localizedDescription]
reason:reason];
*error = blockError;
} else {
NSString *description = [NSString stringWithFormat:@"evaluation block returned `NO` for %@ value '%@'", [event.value class], event.value];
NSString *reason = [NSString stringWithFormat:@"expected to %@ with value %@ '%@', but it did not",
expectation, [event.value class], event.value];
*error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:userInfo
description:description
reason:reason];
}
}
} else if (propertyExpectation.value) {
// Use RestKit comparison magic to match values
success = RKObjectIsEqualToObject(event.value, propertyExpectation.value);
if (! success) {
NSString *description = [NSString stringWithFormat:@"mapped to unexpected %@ value '%@'", [event.value class], event.value];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but instead got %@ '%@'",
expectation, [event.value class], event.value];
if (error) *error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:userInfo
description:description
reason:reason];
}
} else if (propertyExpectation.mapping) {
if ([event.propertyMapping isKindOfClass:[RKRelationshipMapping class]]) {
// Check the mapping that was used to map the relationship
RKMapping *relationshipMapping = [(RKRelationshipMapping *)event.propertyMapping mapping];
success = [relationshipMapping isEqualToMapping:propertyExpectation.mapping];
if (! success) {
NSString *description = [NSString stringWithFormat:@"mapped using unexpected mapping: %@", relationshipMapping];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but was instead mapped using: %@",
expectation, relationshipMapping];
if (error) *error = [self errorForExpectation:expectation
withCode:RKMappingTestValueInequalityError
userInfo:userInfo
description:description
reason:reason];
}
} else {
NSString *description = [NSString stringWithFormat:@"expected a property mapping of type `RKRelationshipMapping` but instead got a `%@`", [propertyExpectation.mapping class]];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but instead of a `RKRelationshipMapping` got a `%@`",
expectation, [propertyExpectation.mapping class]];
if (error) *error = [self errorForExpectation:expectation
withCode:RKMappingTestMappingMismatchError
userInfo:userInfo
description:description
reason:reason];
// Error message here that a relationship was not mapped!!!
return NO;
}
} else {
NSString *description = [NSString stringWithFormat:@"expected a property mapping of type `RKRelationshipMapping` but instead got a `%@`", [expectation.mapping class]];
NSString *reason = [NSString stringWithFormat:@"expected to %@, but instead of a `RKRelationshipMapping` got a `%@`",
expectation, [expectation.mapping class]];
if (error) *error = [self errorForExpectation:expectation
withCode:RKMappingTestEvaluationBlockError
userInfo:userInfo
description:description
reason:reason];
// Error message here that a relationship was not mapped!!!
return NO;
// We only wanted to know that a mapping occured between the keyPaths
success = YES;
}
} else {
// We only wanted to know that a mapping occured between the keyPaths
success = YES;
} else if ([expectation isKindOfClass:[RKConnectionTestExpectation class]]) {
RKConnectionTestExpectation *connectionExpectation = (RKConnectionTestExpectation *)expectation;
RKConnectionDescription *connection = event.connection;
id expectedValue = connectionExpectation.value;
id connectedValue = event.value;
// Check that the connection attributes match
if (connectionExpectation.attributes) {
RKMappingTestCondition([connectionExpectation.attributes isEqualToDictionary:event.connection.attributes], RKMappingTestValueInequalityError, error, @"established connection using unexpected attributes: %@", event.connection.attributes);
}
// Wrong objects
if (expectedValue) {
RKMappingTestCondition(connectedValue, RKMappingTestValueInequalityError, error, @"unexpectedly connected to nil object set (%@)", connectedValue);
if ([connectedValue isKindOfClass:[NSManagedObject class]] && [connectionExpectation.value isKindOfClass:[NSManagedObject class]]) {
// Do a managed object ID comparison
RKMappingTestCondition([[connectedValue objectID] isEqual:[expectedValue objectID]], RKMappingTestValueInequalityError, error, @"connected to unexpected managed object: %@", connectedValue);
} else {
// If we are connecting to a collection of managed objects, do a comparison of object IDs
if (RKObjectIsCollectionContainingOnlyManagedObjects(connectedValue) && RKObjectIsCollectionContainingOnlyManagedObjects(expectedValue)) {
RKMappingTestCondition(RKObjectIsEqualToObject([connectedValue valueForKeyPath:@"objectID"], [expectedValue valueForKeyPath:@"objectID"]), RKMappingTestValueInequalityError, error, @"connected to unexpected %@ value '%@'", [connectedValue class], connectedValue);
} else {
RKMappingTestCondition(RKObjectIsEqualToObject(connectedValue, expectedValue), RKMappingTestValueInequalityError, error, @"connected to unexpected %@ value '%@'", [connectedValue class], connectedValue);
}
}
} else {
RKMappingTestCondition(connectedValue == nil, RKMappingTestValueInequalityError, error, @"unexpectedly connected to non-nil object set (%@)", connectedValue);
}
return YES;
}
return success;
}
@@ -342,9 +365,9 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
}
}
- (void)verifyExpectation:(RKMappingTestExpectation *)expectation
- (void)verifyExpectation:(RKPropertyMappingTestExpectation *)expectation
{
RKMappingTestEvent *event = [self eventMatchingKeyPathsForExpectation:expectation];
RKMappingTestEvent *event = [self eventMatchingExpectation:expectation];
if (event) {
// Found a matching event, check if it satisfies the expectation
NSError *error = nil;
@@ -359,7 +382,7 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
} else {
// No match
[NSException raise:NSInternalInconsistencyException format:@"%@: expectation not satisfied: %@, but did not.",
[self description], [expectation mappingDescription]];
[self description], [expectation summary]];
}
}
@@ -367,7 +390,7 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
{
[self performMapping];
for (RKMappingTestExpectation *expectation in self.expectations) {
for (RKPropertyMappingTestExpectation *expectation in self.expectations) {
[self verifyExpectation:expectation];
}
}
@@ -378,18 +401,20 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
{
[self performMapping];
for (RKMappingTestExpectation *expectation in self.expectations) {
for (RKPropertyMappingTestExpectation *expectation in self.expectations) {
if (! [self evaluateExpectation:expectation error:nil]) return NO;
}
return YES;
}
- (BOOL)evaluateExpectation:(RKMappingTestExpectation *)expectation error:(NSError **)error
- (BOOL)evaluateExpectation:(id)expectation error:(NSError **)error
{
NSParameterAssert(expectation);
NSAssert([expectation isKindOfClass:[RKPropertyMappingTestExpectation class]] || [expectation isKindOfClass:[RKConnectionTestExpectation class]], @"Must be an instance of `RKPropertyMappingTestExpectation` or `RKConnectionTestExpectation`");
[self performMapping];
RKMappingTestEvent *event = [self eventMatchingKeyPathsForExpectation:expectation];
RKMappingTestEvent *event = [self eventMatchingExpectation:expectation];
if (event) {
if (! [self event:event satisfiesExpectation:expectation error:error]) {
return NO;
@@ -398,8 +423,8 @@ NSString * const RKMappingTestVerificationFailureException = @"RKMappingTestVeri
if (error) {
NSDictionary *userInfo = @{
RKMappingTestExpectationErrorKey : expectation,
NSLocalizedDescriptionKey : [NSString stringWithFormat:@"expected to %@, but did not.", [expectation mappingDescription]],
NSLocalizedFailureReasonErrorKey : [NSString stringWithFormat:@"%@: %@, but did not.", [self description], [expectation mappingDescription]]
NSLocalizedDescriptionKey : [NSString stringWithFormat:@"expected to %@, but did not.", [expectation summary]],
NSLocalizedFailureReasonErrorKey : [NSString stringWithFormat:@"%@: %@, but did not.", [self description], [expectation summary]]
};
*error = [NSError errorWithDomain:RKMappingTestErrorDomain code:RKMappingTestUnsatisfiedExpectationError userInfo:userInfo];
};

View File

@@ -1,5 +1,5 @@
//
// RKMappingTestExpectation.h
// RKPropertyMappingTestExpectation.h
// RestKit
//
// Created by Blake Watters on 2/17/12.
@@ -18,7 +18,7 @@
// limitations under the License.
//
@class RKMapping, RKPropertyMapping, RKMappingTestExpectation;
@class RKMapping, RKPropertyMapping, RKPropertyMappingTestExpectation;
/**
@typedef RKMappingTestExpectationEvaluationBlock
@@ -28,35 +28,18 @@
@param mappedValue The value that was mapped.
@param error A pointer to an error object that is to be set in the event that the expectation evaluates negatively. If left to `nil`, a generic error will be generated.
*/
typedef BOOL (^RKMappingTestExpectationEvaluationBlock)(RKMappingTestExpectation *expectation, RKPropertyMapping *mapping, id mappedValue, NSError **error);
/**
@define RKMappingTestExpectationTestCondition
@abstract Tests a condition and returns `NO` and error if it is not true.
@discussion This is a useful macro when constructing mapping test evaluation blocks. It will test a condition and return `NO` as well as construct an error. This is meant to be used **only** within the body of a `RKMappingTestExpectationEvaluationBlock` object.
@param condition The condition to test.
@param error The NSError object to put the error string into. May be nil, but should usually be the error parameter from the expectation evaluation block.
@param ... A string describing the error.
*/
#define RKMappingTestExpectationTestCondition(condition, error, ...) ({ \
if (!(condition)) { \
if (error) { \
*error = [NSError errorWithDomain:RKMappingTestErrorDomain code:RKMappingTestEvaluationBlockError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:__VA_ARGS__], NSLocalizedDescriptionKey, nil]]; \
} \
return NO; \
} \
})
typedef BOOL (^RKMappingTestExpectationEvaluationBlock)(RKPropertyMappingTestExpectation *expectation, RKPropertyMapping *mapping, id mappedValue, NSError **error);
/**
An `RKMappingTestExpectation` object defines an expected mapping event that should occur during the execution of a `RKMappingTest`.
@see `RKMappingTest`
*/
@interface RKMappingTestExpectation : NSObject
@interface RKPropertyMappingTestExpectation : NSObject
///-----------------------------------------------------------------------------
///----------------------------
/// @name Creating Expectations
///-----------------------------------------------------------------------------
///----------------------------
/**
Creates and returns a new expectation specifying that a key path in a source object should be mapped to another key path on a destination object. The value mapped is not evaluated.
@@ -65,7 +48,7 @@ return NO; \
@param destinationKeyPath A key path on the destination object that should be mapped onto.
@return An expectation specifying that sourceKeyPath should be mapped to destinationKeyPath.
*/
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath;
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath;
/**
Creates and returns a new expectation specifying that a key path in a source object should be mapped to another key path on a destination object with a given value.
@@ -75,7 +58,7 @@ return NO; \
@param value The value that is expected to be assigned to the destination object at destinationKeyPath.
@return An expectation specifying that sourceKeyPath should be mapped to destinationKeyPath with value.
*/
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath value:(id)value;
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath value:(id)value;
/**
Creates and returns a new expectation specifying that a key path in a source object should be mapped to another key path on a destinaton object and that the attribute mapping and value should evaluate to true with a given block.
@@ -85,7 +68,7 @@ return NO; \
@param evaluationBlock A block with which to evaluate the success of the mapping.
@return An expectation specifying that sourceKeyPath should be mapped to destinationKeyPath with value.
*/
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath evaluationBlock:(RKMappingTestExpectationEvaluationBlock)evaluationBlock;
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath evaluationBlock:(RKMappingTestExpectationEvaluationBlock)evaluationBlock;
/**
Creates and returns a new expectation specifying that a key path in a source object should be mapped to another key path on a destinaton object using a specific object mapping for the relationship.
@@ -95,11 +78,11 @@ return NO; \
@param mapping An object mapping that is expected to be used for mapping the nested relationship.
@return An expectation specifying that sourceKeyPath should be mapped to destinationKeyPath using a specific object mapping.
*/
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath mapping:(RKMapping *)mapping;
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath mapping:(RKMapping *)mapping;
///-----------------------------------------------------------------------------
///-------------------------
/// @name Expectation Values
///-----------------------------------------------------------------------------
///-------------------------
/**
Returns a keyPath on the source object that a value should be mapped from.
@@ -133,6 +116,6 @@ return NO; \
@return A string describing the expected sourceKeyPath to destinationKeyPath mapping.
*/
- (NSString *)mappingDescription;
- (NSString *)summary;
@end

View File

@@ -18,10 +18,10 @@
// limitations under the License.
//
#import "RKMappingTestExpectation.h"
#import "RKPropertyMappingTestExpectation.h"
#import "RKPropertyMapping.h"
@interface RKMappingTestExpectation ()
@interface RKPropertyMappingTestExpectation ()
@property (nonatomic, copy, readwrite) NSString *sourceKeyPath;
@property (nonatomic, copy, readwrite) NSString *destinationKeyPath;
@property (nonatomic, strong, readwrite) id value;
@@ -29,20 +29,20 @@
@property (nonatomic, strong, readwrite) RKMapping *mapping;
@end
@implementation RKMappingTestExpectation
@implementation RKPropertyMappingTestExpectation
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath
{
RKMappingTestExpectation *expectation = [self new];
RKPropertyMappingTestExpectation *expectation = [self new];
expectation.sourceKeyPath = sourceKeyPath;
expectation.destinationKeyPath = destinationKeyPath;
return expectation;
}
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath value:(id)value
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath value:(id)value
{
RKMappingTestExpectation *expectation = [self new];
RKPropertyMappingTestExpectation *expectation = [self new];
expectation.sourceKeyPath = sourceKeyPath;
expectation.destinationKeyPath = destinationKeyPath;
expectation.value = value;
@@ -50,9 +50,9 @@
return expectation;
}
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath evaluationBlock:(RKMappingTestExpectationEvaluationBlock)evaluationBlock
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath evaluationBlock:(RKMappingTestExpectationEvaluationBlock)evaluationBlock
{
RKMappingTestExpectation *expectation = [self new];
RKPropertyMappingTestExpectation *expectation = [self new];
expectation.sourceKeyPath = sourceKeyPath;
expectation.destinationKeyPath = destinationKeyPath;
expectation.evaluationBlock = evaluationBlock;
@@ -60,9 +60,9 @@
return expectation;
}
+ (RKMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath mapping:(RKMapping *)mapping
+ (RKPropertyMappingTestExpectation *)expectationWithSourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath mapping:(RKMapping *)mapping
{
RKMappingTestExpectation *expectation = [self new];
RKPropertyMappingTestExpectation *expectation = [self new];
expectation.sourceKeyPath = sourceKeyPath;
expectation.destinationKeyPath = destinationKeyPath;
expectation.mapping = mapping;
@@ -70,7 +70,7 @@
return expectation;
}
- (NSString *)mappingDescription
- (NSString *)summary
{
return [NSString stringWithFormat:@"map '%@' to '%@'", self.sourceKeyPath, self.destinationKeyPath];
}
@@ -88,7 +88,7 @@
self.sourceKeyPath, self.destinationKeyPath, self.mapping];
}
return [self mappingDescription];
return [self summary];
}
@end