aryaxtからのWebServiceアンサーの上にビルドします。ここでは、別のテストで異なる結果を得るための少しトリックです。
まず、あなたが右のテストの前に、必要な答えを格納するために使用されるシングルトンオブジェクトが必要 TestConfiguration.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
void MethodSwizzle(Class c, SEL orig, SEL new);
@interface TestConfiguration : NSObject
@property(nonatomic,strong) NSMutableDictionary *results;
+ (TestConfiguration *)sharedInstance;
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector;
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector;
@end
TestConfiguration.m
#import "TestConfiguration.h"
void MethodSwizzle(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
@implementation TestConfiguration
- (id)init
{
self = [super init];
if (self) {
self.results = [[NSMutableDictionary alloc] init];
}
return self;
}
+ (TestConfiguration *)sharedInstance
{
static TestConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[TestConfiguration alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
[self.results setObject:result
forKey:[[className stringByAppendingString:@":"] stringByAppendingString:selectorName]];
}
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
return [self.results objectForKey:[[className stringByAppendingString:@":"] stringByAppendingString:selectorName]];
}
@end
次に、モックメソッドを定義するための "Mock"カテゴリを定義します。
#import "MyWebService+Mock.h"
#import "TestConfiguration.h"
@implementation MyWebService (Mock)
-(void)mockFetchEntityWithId:(NSNumber *)entityId
success:(void (^)(Entity *entity))success
failure:(void (^)(NSError *error))failure
{
Entity *response = (Entity *)[[TestConfiguration sharedInstance] getResultForCallToObject:self selector:@selector(fetchEntityWithId:success:failure:)];
if (response == nil)
{
failure([NSError errorWithDomain:@"entity not found" code:1 userInfo:nil]);
}
else{
success(response);
}
}
@end
そして最後に、テストそのものには、セットアップでモックメソッドをスウィズルだろう、とコール
MyServiceTest.m
- (void)setUp
{
[super setUp];
//swizzle webservice method call to mock object call
MethodSwizzle([MyWebService class], @selector(fetchEntityWithId:success:failure:), @selector(mockFetchEntityWithId:success:failure:));
}
- (void)testWSMockedEntity
{
/* mock an entity response from the server */
[[TestConfiguration sharedInstance] setNextResult:[Entity entityWithId:1]
forCallToObject:[MyWebService sharedInstance]
selector:@selector(fetchEntityWithId:success:failure:)];
// now perform the call. You should be able to call STAssert in the blocks directly, since the success/error block should now be called completely synchronously.
}
備考前に、各テストで期待される答えを定義します:私の例では、TestConfigurationはclass/selectorをobject/selectorの代わりにキーとして使います。つまり、クラスのすべてのオブジェクトがセレクタに対して同じ回答を使用します。 Webサービスはしばしばシングルトンであるため、これがあなたのケースです。しかし、クラスの代わりにobjetのメモリアドレスを使用してオブジェクト/セレクタに変更する必要があります
"TestConfiguration"のようなシングルトンオブジェクトを持っていないと、呼び出しごとに偽の結果を取得することはできませんモックカテゴリ内で? –
任意の内部構造をプロパティとして公開することができます。したがって、必要に応じてクラスを構成できます。 –