ニフティクラウド mobile backend(mBaaS)のSDKテスト用に、
YAMLとJSONファイルでモックサーバーが定義できれば、
AndroidでもiOSでも使いまわせるという作戦です。
前回のAndroidのUnitTest用モックサーバーをYAMLとJSONから作るのをiOSにも展開しました
Androidのほうはクエリストリングとリクエストボディで
レスポンスを切り替える改修もしているので別途どこかで共有します
YAMLを扱うライブラリ
- mirek/YAML.frameworkを利用する
target :YOUR_TARGET_NAME do
pod 'Specta'
pod 'Expecta'
pod 'OHHTTPStubs'
pod 'YAML-Framework'
end
テストコード
- SpectaやらOHHTTPStubsなどを使っています
- beforeAllでYAMLとJSONファイルを読み込みます
- 基本はYAMLファイルと一致するものをfor文で探しているだけです
ポイント
- SpectaがXCTestベースなので、bundleを取得するときにmainBundleだとうまくいかない
- @keroxpさんのXCTestの中でNSBundleにアクセスするを参考にしています
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"mbaas" ofType:@"yml"];
NSData *ymlData = [NSData dataWithContentsOfFile:path];
- OHHTTPStubsのレスポンス作成は、都度stubRequestsPassingTest:withStubResponse:メソッドを呼び出さない方法にしました
- @yimajoさんのOHHTTPStubsを使ったiOSアプリ開発の進め方を参考にしてます
YAMLとJSONファイル
- resource/yamlグループとresource/jsonグループをXcodeでつくりましたが、あんまり関係ないです
- Build PhasesのCopy Bundle Resourcesに追加されていればOKだと思います
YAMLファイル
---
request:
url: /2013-09-01/classes/TestClass/testObjectId
method: GET
response:
status: 201
file: valid_get_request.json
JSONファイル
{"objectId":"testObjectId","createDate":"2015-08-05T00:00:00.000Z","updateDate":"2015-08-05T00:00:00.000Z","acl":{"*":{"read":true, "write":true}}}
テストコード全文
#import <Specta/Specta.h>
#import <Expecta/Expecta.h>
#import <OHHTTPStubs/OHHTTPStubs.h>
#import <YAML-Framework/YAMLSerialization.h>
SpecBegin(StubTest)
beforeAll(^{
//Yaml file reading
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"mbaas" ofType:@"yml"];
NSData *ymlData = [NSData dataWithContentsOfFile:path];
NSError *error = nil;
NSMutableArray *yaml = [YAMLSerialization objectsWithYAMLData:ymlData
options: kYAMLReadOptionStringScalars
error: &error];
//set default response
NSDictionary *responseDic = @{@"code":@"E404001",
@"error":@"Not found request"};
NSData *jsonResponse = [NSJSONSerialization dataWithJSONObject:responseDic options:0 error:nil];
//CreateStub
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
for (NSDictionary *stub in yaml){
NSDictionary *stubRequest = [stub objectForKey:@"request"];
if (![[stubRequest objectForKey:@"url"] isEqualToString:request.URL.path]){
continue;
}
if (![[stubRequest objectForKey:@"method"] isEqualToString:request.HTTPMethod]){
continue;
}
return YES;
}
return NO;
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
for (NSDictionary *stub in yaml){
NSDictionary *stubRequest = [stub objectForKey:@"request"];
if (![[stubRequest objectForKey:@"url"] isEqualToString:request.URL.path]){
continue;
}
if (![[stubRequest objectForKey:@"method"] isEqualToString:request.HTTPMethod]){
continue;
}
NSString *jsonFileName = [[stub objectForKey:@"response"] objectForKey:@"file"];
NSArray *fileNameArray = [jsonFileName componentsSeparatedByString:@"."];
NSString *jsonPath = [[NSBundle bundleForClass:[self class]] pathForResource:fileNameArray[0]
ofType:fileNameArray[1]];
return [OHHTTPStubsResponse responseWithData:[NSData dataWithContentsOfFile:jsonPath]
statusCode:[[[stub objectForKey:@"response"] objectForKey:@"status"] intValue]
headers:@{@"Content-Type":@"application/json"}];;
}
return [OHHTTPStubsResponse responseWithData:jsonResponse
statusCode:404
headers:@{@"Content-Type":@"application/json"}];
}];
});
describe(@"OHHTTPStubs", ^{
it(@"should return stub response", ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/2013-09-01/classes/TestClass/testObjectId"]];
NSHTTPURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:nil];
expect([responseDic objectForKey:@"objectId"]).to.equal(@"testObjectId");
});
});
afterAll(^{
[OHHTTPStubs removeAllStubs];
});
SpecEnd
さいごに
- デフォルトのレスポンスを作成するのであれば、リクエストをチェックする部分はすぐtrueを返してもいい気がしています。YAMLファイルの中身を2回チェックしているので。。。
//このfor文が2回実装されている。。。
for (NSDictionary *stub in yaml){
}
- (まだ)URLのパスとリクエストメソッドしかチェックしていません
- AndroidもiOSもいつかライブラリにしてしまいたいです