@blog.justoneplanet.info

日々勉強

GHAsyncTestCaseを使ってASIHTTPRequestの非同期通信のテストをする

ドキュメントが分かりにくかったので自分で書いてみた。

■テスト対象のクラス

ASIHTTPRequestを使った普通のクラス。

MyHTTP.h

#import <Foundation/Foundation.h>
#import "MyHTTPDelegate.h"

@class ASIHTTPRequest;
@class MyHTTPDelegate;

@interface MyHTTP : NSObject {
	id <MyHTTPDelegate> delegate;
}
@property (nonatomic, retain) id delegate;
- (void)loadData;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
@end

MyHTTP.m

以下のように実装する。ASIHTTPRequestを使った事があるならばお手の物だ。

#import "MyHTTP.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"

// 今回は触れないけど通信するので圏外判定に使っている
#import "UIDevice-Reachability.h"
#import "Reachability.h"

#define URL @"http://example.org/api/data"

@implementation MyHTTP
@synthesize delegate;

// 通信して更新する
- (void)loadData
{
    if([[UIDevice currentDevice] networkAvailable] == YES){
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:URL]];
        [request setDelegate:self];
        [request setRequestMethod:@"GET"];
        [request startAsynchronous];// 非同期通信の開始
    }
    else{
        // 圏外の時 = cahceから再現させる
    }
}

#pragma mark -
#pragma mark ASIHTTPRequestDelegate
- (void)requestFinished:(ASIHTTPRequest *)request
{
    SBJsonParser *parser = [[SBJsonParser alloc] init];
    NSArray *json = [parser objectWithString:[request responseString] error:nil];
    if(json != nil && [json count] > 0){// success
        [delegate reflectData:json];
    }
    [parser release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
    NSError *error = [request error];
}
@end

■テストクラス

MyHTTPTest.h

以下のようにGHAsyncTestCaseを継承する。

#import <GHUnitIOS/GHUnit.h>

@class MyHTTP;

@interface MyHTTPTest : GHAsyncTestCase {
    MyHTTP *http;
}
@property (nonatomic, retain) MyHTTP *http;
@end

MyHTTPTest.m

以下のように実装する。

#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.1];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

GHAssert系のメソッドはテストが失敗した場合にExceptionを発生させるので、上述のようにcatchしないとユニットテスト自体がクラッシュする。ちなみに以下のように書いても問題なくテストできた。


#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [http loadData];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

performSelectorメソッドの有用性が分からない。

■おまけ

実はMyHTTPクラスには通信ができなかった時の事を考慮して、キャッシュから呼び出す機能がある。以下のようにSELをプロパティに持たせる事で通知先を分岐するようにした。

#import "MyHTTPProxyTest.h"
#import "MyHTTPProxy.h"

@implementation MyHTTPProxyTest
@synthesize http;
@synthesize selector;

- (void)setUpClass
{
    http = [[MyHTTPProxy alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadCache
{
    [self setSelector:@selector(testLoadCache)];
    [self prepare];
    [self performSelector:@selector(startLoadCache) withObject:nil afterDelay:0.01];
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)testLoadData
{
    [self setSelector:@selector(testLoadData)];
    [self prepare];
    
    // Do asynchronous task here
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.01];
    
    // Wait for notify
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startLoadCache
{
    [http loadCache];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer");
        [self notify:kGHUnitWaitStatusSuccess forSelector:[self selector]];
    }
    @catch (NSException *exception) {
        [self notify:kGHUnitWaitStatusFailure forSelector:[self selector]];
    }
}
@end

コメントはまだありません»

No comments yet.

RSS feed for comments on this post.TrackBack URL

Leave a comment