33

私は、一連のリモート Web サービスとの通信を主な目的とする iOS アプリに取り組んでいます。統合テストのために、予測可能な結果を​​持つある種の偽の Web サービスに対して自分のアプリを実行できるようにしたいと考えています。

これまでのところ、2 つの提案を見てきました。

  1. クライアントに静的な結果を提供する Web サーバーを作成します (例:ここ)。
  2. コンパイル時のフラグに基づいて、Web サービスまたはローカル ファイルから応答をロードするコードを呼び出す、さまざまな Web サービス通信コードを実装します ( exampleおよびのファイル)。

このアプローチのそれぞれについてコミュニティがどう考えているか、また、このワークフローをサポートするツールが存在するかどうかに興味があります。

更新:具体的な例を挙げましょう。ユーザー名とパスワードを受け取るログインフォームがあります。2つの条件を確認したい:

  1. wronguser@blahblah.comがログインを拒否され、
  2. rightuser@blahblah.comログインに成功しました。

そのため、ユーザー名パラメーターをチェックして適切な応答をスローするコードが必要です。「偽の Web サービス」に必要なロジックはこれですべてだと思います。これをきれいに管理するにはどうすればよいですか?

4

5 に答える 5

25

Nocillaを使用することをお勧めします。Nocilla は、単純な DSL を使用して HTTP リクエストをスタブ化するためのライブラリです。

google.com から 404 を返したいとしましょう。あなたがしなければならないことは次のとおりです。

stubRequest(@"GET", "http://www.google.com").andReturn(404); // Yes, it's ObjC

その後、google.com への HTTP は 404 を返します。

POST を特定の本文とヘッダーに一致させ、定型応答を返す、より完全な例:

stubRequest(@"POST", @"https://api.example.com/dogs.json").
withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
withBody(@"{\"name\":\"foo\"}").
andReturn(201).
withHeaders(@{@"Content-Type": @"application/json"}).
withBody(@"{\"ok\":true}");

任意のリクエストに一致し、任意のレスポンスを偽造できます。詳細については、README を確認してください。

他のソリューションよりも Nocilla を使用する利点は次のとおりです。

  • これは速い。実行する HTTP サーバーがありません。テストは非常に高速に実行されます。
  • 管理するクレイジーな依存関係はありません。その上で、CocoaPods を使用できます。
  • それはよくテストされています。
  • コードの理解と保守を非常に簡単にする優れた DSL。

主な制限は、AFNetworking、MKNetworkKit、プレーンな NSURLConnection など、NSURLConnection の上に構築された HTTP フレームワークでのみ機能することです。

お役に立てれば。他に何か必要があれば、私がお手伝いします。

于 2013-03-28T03:16:07.617 に答える
7

Objective-C を使用していると仮定します。Objective-C の場合、 OCMockはモック/単体テストに広く使用されています (2 番目のオプション)。

最後に OCMock を使用したのは 1 年以上前ですが、私が覚えている限り、これは本格的なモッキング フレームワークであり、以下で説明するすべてのことを実行できます。

モックに関する重要な点の 1 つは、オブジェクトの実際の機能を最大限に活用できることです。「空の」モック (すべてのメソッドがオブジェクトになりますが、何もしません) を作成し、テストで必要なメソッドだけをオーバーライドできます。これは通常、モックに依存する他のオブジェクトをテストするときに行われます。

または、実際のオブジェクトの動作として機能するモックを作成し、そのレベルでテストしたくないいくつかのメソッド (たとえば、実際にデータベースにアクセスするメソッド、ネットワーク接続を必要とするメソッドなど) をスタブ化することもできます。これは通常、モックされたオブジェクト自体をテストするときに行われます。

一度だけモックを作成するわけではないことを理解することが重要です。すべてのテストは、テスト対象に基づいて、同じオブジェクトのモックを新たに作成できます。

モックに関するもう 1 つの重要な点は、シナリオ (一連の呼び出し) とそれらについての「期待」 (舞台裏でどのメソッドをどのパラメーターでどの順序で呼び出すか) を「記録」してから、「再生」できることです。シナリオ - 期待が満たされない場合、テストは失敗します。これが、クラシック TDD とモックスト TDD の主な違いです。これには長所と短所があります (Martin Fowler の記事を参照)。

具体的な例を考えてみましょう (Objective C ではなく、C++ または Java に似た疑似構文を使用します)。

LoginForm入力されたログイン情報を表すクラスのオブジェクトがあるとします。(とりわけ) setName(String)setPassword(String)bool authenticateUser()、および のメソッドがありAuthenticator* getAuthenticator()ます。

Authenticatorまた、 (とりわけ)メソッドbool isRegistered(String user)bool authenticate(String user, String password)、およびを持つクラスのオブジェクトもありますbool isAuthenticated(String user)

簡単なシナリオをテストする方法は次のとおりです。

MockLoginForm上記の 4 つを除くすべてのメソッドを空にしてモックを作成します。最初の 3 つの方法は、実際のLoginForm実装を使用します。getAuthenticator()を返すためにスタブアウトされMockAuthenticatorます。

MockAuthenticator偽のデータベース (内部データ構造やファイルなど) を使用して 3 つのメソッドを実装するモックを作成します。データベースにはタプルが 1 つだけ含まれます: ('rightuser','rightpassword').

TestUserNotRegistered

リプレイ シナリオ:

MockLoginForm.setName('wronuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();

期待:

getAuthenticator() is called
MockAuthenticator.isRegistered('wrognuser') is called and returns 'false'

テスト間違ったパスワード

リプレイ シナリオ:

MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();

期待:

getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','foo') is called and returns 'false'

テストログインOK

リプレイ シナリオ:

MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('rightpassword');
MockLoginForm.authenticate();
result = MockAuthenticator.isAuthenticated('rightuser')

期待:

getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','rightpassword') is called and returns 'true'
result is 'true'

これが役立つことを願っています。

于 2012-06-04T21:08:20.503 に答える
6

NSURLProtocol サブクラスを使用すると、モック Web サービスを非常に効果的に作成できます。

ヘッダ:

@interface MyMockWebServiceURLProtocol : NSURLProtocol
@end

実装:

@implementation MyMockWebServiceURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    return [[[request URL] scheme] isEqualToString:@"mymock"];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
    return [[a URL] isEqual:[b URL]];
}

- (void)startLoading
{
    NSURLRequest *request = [self request];
    id <NSURLProtocolClient> client = [self client];
    NSURL *url = request.URL;
    NSString *host = url.host;
    NSString *path = url.path;
    NSString *mockResultPath = nil;
    /* set mockResultPath here … */
    NSString *fileURL = [[NSBundle mainBundle] URLForResource:mockResultPath withExtension:nil];
    [client URLProtocol:self
 wasRedirectedToRequest:[NSURLRequest requestWithURL:fileURL]
       redirectResponse:[[NSURLResponse alloc] initWithURL:url
                                                  MIMEType:@"application/json"
                                     expectedContentLength:0
                                          textEncodingName:nil]];
    [client URLProtocolDidFinishLoading:self];
}

- (void)stopLoading
{
}

@end

興味深いルーチンは -startLoading です。このルーチンでは、クライアントをそのファイル URL にリダイレクトする前に、リクエストを処理し、アプリ バンドル内のレスポンスに対応する静的ファイルを見つける必要があります。

プロトコルをインストールします

[NSURLProtocol registerClass:[MyMockWebServiceURLProtocol class]];

そして、次のような URL で参照します。

mymock://mockhost/mockpath?mockquery

これは、リモート マシン上またはアプリ内でローカルに実際の Web サービスを実装するよりもはるかに簡単です。トレードオフは、HTTP 応答ヘッダーのシミュレートがはるかに困難になることです。

于 2012-06-11T12:06:36.307 に答える
6

OHTTPStubsは、多くの注目を集めている、やりたいことを実行するための非常に優れたフレームワークです。彼らの github readme から:

OHTTPStubs は、ネットワーク リクエストを非常に簡単にスタブ化するために設計されたライブラリです。それはあなたを助けることができます:

  • 偽のネットワーク データ (ファイルからスタブ化) を使用してアプリをテストし、低速ネットワークをシミュレートして、ネットワーク状態が悪い場合のアプリケーションの動作を確認します。
  • フィクスチャからの偽のネットワーク データを使用する単体テストを記述します。

NSURLConnection新しい iOS7/OSX.9 (1.x と 2.x の両方)、または Cocoa の URL ローディング システムを使用するネットワーク フレームワークで動作します。NSURLSessionAFNetworking

OHHTTPStubs ヘッダーは、ヘッダー ファイル内の Appledoc のような / Headerdoc のようなコメントを使用して完全に文書化されています。また、ここでオンライン ドキュメントを読むこともできます

次に例を示します。

[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
    return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
    // Stub it with our "wsresponse.json" stub file
    NSString* fixture = OHPathForFileInBundle(@"wsresponse.json",nil);
    return [OHHTTPStubsResponse responseWithFileAtPath:fixture
              statusCode:200 headers:@{@"Content-Type":@"text/json"}];
}];

wiki ページで追加の使用例を見つけることができます。

于 2014-05-22T18:24:36.540 に答える
2

オプション 1 に関しては、過去に CocoaHTTPServer を使用してサーバーを OCUnit テストに直接埋め込んでこれを実行しました。

https://github.com/robbiehanson/CocoaHTTPServer

ここにユニットテストでこれを使用するためのコードを掲載しました: https://github.com/quellish/UnitTestHTTPServer

結局のところ、HTTP は設計上、要求/応答だけです。

モック HTTP サーバーを作成するか、コードでモック Web サービスを作成するかに関係なく、Web サービスをモックすることは、ほぼ同じ量の作業になります。テストする X コード パスがある場合は、モックで処理する少なくとも X コード パスがあります。

オプション 2 の場合、Web サービスと通信しない Web サービスをモックするには、代わりに既知の応答を持つモック オブジェクトを使用します。 [MyCoolWebService performLogin:username withPassword:password]

あなたのテストでは、

[MyMockWebService performLogin:username withPassword:password] 重要な点は、MyCoolWebService と MyMockWebService が同じコントラクトを実装することです (objective-c では、これはプロトコルになります)。OCMock には、作業を開始するためのドキュメントが多数用意されています。

ただし、統合テストの場合は、QA/ステージング環境などの実際の Web サービスに対してテストする必要があります。あなたが実際に説明していることは、統合テストよりも機能テストのように聞こえます。

于 2012-06-08T05:07:05.343 に答える