LoginSignup
24
24

More than 5 years have passed since last update.

[iOS] 日時に依存する機能のユニットテストを簡単に&わかりやすく書けるカテゴリ

Last updated at Posted at 2015-01-05

アプリを開発していると、現在日時が影響するような機能を作る機会が多いです。例えば...

  • 時間限定でキャンペーンを表示したい
  • クリスマス限定デザイン等の季節限定のイベントや機能

などなどですね。そんな機能を開発しているとき、テストケースを書く際にNSDateOCMockであれこれがんばってたのですが、手間がかかったり、テストが見づらくなることが多かったです。

そこで、そんな日時に関する機能のテストケースを書くのをサポートするNSDateのカテゴリを作ってみました!

NSDate+SRGFekable

導入してみる

Podfileに以下の行を追加します。

Podfile
pod 'NSDate+SRGFekable'

さらにSwift&テストプロジェクトで利用する場合は上に加えてlink_withに以下の行を追加します。

Podfile
# swiftの場合必要、本プロジェクトとテストプロジェクトをリンクさせておく必要がある
# プロジェクトが"MyApp"だとすると..
link_with 'MyAppExample', 'MyAppTests'

Podfile更新後pod updateしたら、Objective-Cの場合は利用するクラスで、Swiftの場合はBridging-Header.hで以下のファイルをインポートすると利用できます。

#import "NSDate+SRGFekable.h"

なにができるの?

NSDateには現在時刻を元に値が決まるメソッドがありますが、それらの返り値を簡単に偽装(fake)することができます。
例えば利用例は以下のようになります。

簡単な日時の偽装
// 以下の1行を追加すると...
[NSDate fakeWithString:@"2014/12/27 10:00:00"];

// これ以降プロセス内でNSDateで取得する現在時刻が上で偽装(fake)した日付に
 NSLog(@"now:%@",[NSDate date]); // -> now:2014/12/27 10:00:00

上のコードのようにfakeWithStringを呼ぶと、現在時刻に依存するNSDateのメソッド+date+dateWithTimeIntervalSinceNow:-initWithTimeIntervalSinceNow:-init-timeIntervalSinceNowの返す値がfakeした値になります。

(実装方法としてはfakeWithStringが呼ばれたタイミングでMethod Swizzling を使ってNSDateのメソッドを差し替えています。)

簡単な利用例 / UnitTestで使ってみる

このカテゴリを利用すると、内部で時刻に依存した処理があるようなクラスのUnitTestも簡単に書くことができます。

例えばテスト対象としてSeasonalEventクラスがあり、そこにisXmasというメソッドがあったとします。isXmasは日付が12/25の時のみTRUEを返し、それ以外はFALSEを返す仕様だとすると...

NSDate+SRGFekable.hを利用してXCTestCaseでテストケースを書くと以下のように書くことができます。

SeasonalEventのテストケース
....
- (void)testIsXmas {
    SeasonalEvent *seasonalEvent = [SeasonalEvent new];

    // 12/20はクリスマスではない
    [NSDate fakeWithString:@"2014/12/20 10:00:00"];
    XCTAssertFalse( seasonalEvent.isXmas );

     // 12/25はクリスマス
    [NSDate fakeWithString:@"2014/12/25 10:00:00"];
    XCTAssertTrue( seasonalEvent.isXmas );
}
...

OCMockなどでmockを組み立てるよりもシンプルにかけると思います。
fakeWithString以外にも、NSDate周りのテストを、より簡単に行えるメソッドを用意しているので、以下で詳細を説明します。

詳細な使い方

fakeWithString - 文字列からfake

fakeWithStringを使うと文字列で日付を指定できます。timeZoneはデフォルトの場合はシステムで利用しているtimeZoneとなります。

fakeWithString
// タイムゾーンを指定しない場合、端末で設定されているタイムゾーンでfake
[NSDate fakeWithString:@"2014/12/20 10:00:00"];

// タイムゾーンを明示的に指定することもできます
[NSDate fakeWithString:@"2014/12/26 10:00:00"
              timeZone:[NSTimeZone timeZoneWithName:@"Asia/Tokyo"]
];

fakeWithDate - NSDateインスタンスからfake

fakeWithDate を使うとNSDate`のインスタンスをもとにfakeすることもできます。

fakeWithDate
// 100秒後を指すNSDateを作成
NSDate *aDate = [NSDate dateWithTimeIntervalSinceNow:100];

// 現在時刻がaDateの指す時間になるようにfake
[NSDate fakeWithDate:aDate];

fakeWithDelta - 差分時間でfake

fakeWithDeltaを使うと、差分の時間を指定してfakeすることができます。

fakeWithDelta
// 現在時刻から60秒進める
[NSDate fakeWithDelta:60];

// さらに120秒進める( total 180秒 )
[NSDate fakeWithDelta:120];

// 30秒巻き戻す( total 150秒 )
[NSDate fakeWithDelta:-30];

fake状態に関するメソッド

stopFaking
// fake状態を解除。NSDateを元の振る舞いに戻す
[NSDate stopFaking];

// fakeしているかどうかの状態を取得
BOOL doFaking = [NSDate doFaking];

freezeオプション

上で紹介した3つのfakeXXX系メソッドは全てfreezeというBOOLのオプションをつけることができます。freezeのデフォルト値はYESです。

freezeYESの時、fakeした時刻で時間は完全にとまっています。
freezeNOの場合は、時間の経過に合わせてfakeした時間が進みます。

これはUnitTestでは、あまり利用しないかもしれませんが、実機での動作確認をfakeを用いて行うことなどを想定しています。

freeze-option
// freeze:YESにすると、指定した時刻のまま常に同じ時刻を返す(デフォルトの動作)
[NSDate fakeWithString:@"2014/12/27 10:00:00" freeze:YES];
   // -> 上の処理から10秒たっても[NSDate date]の値は10時ジャストのまま

// freeze:NOにすると、指定から時間が進んでいく
[NSDate fakeWithString:@"2014/12/27 10:00:00" freeze:NO];
   // -> 上の処理から10秒たつと[NSDate date]の値は10:00:10になる

おまけ - 単体テスト以外でも使ってみる

UnitTestの利用がメインだと思いますが、実機での動作確認に使うこともできます。
その際に、GUIで日付が変えれる機能があると便利だと思い、変更用のControllerも同梱しています。

Controllerの呼び出し方法
#import "SRGFakableViewController.h"
...
[SRGFakableViewController showOn:self freezeOnFake:NO];

引数showOn:には遷移元のUIViewControllerのインスタンスを渡します。なのでcontroller内から呼ぶ場合はselfを渡しておけばOKです。 freezeOnFakeの値は上で説明したfreezeの挙動と同じで、偽装した時刻を停止させるかどうかのフラグです。

showOnが実行されると以下のような画面が表示されます。

FakeFromControllerImage.png

この画面から時刻を選択してDo Fake!ボタンをタップすると指定した日付で時刻が偽装されます。 閉じる時は左上のDoneで画面を閉じれます。

(ただ、UI周りのライブラリなどのコア部分がNSDateに依存しているところがあるため、実際の動作でNSDateの振る舞いを固定すると、正常に動作しない場合もあると思います。)

まとめ

以上、NSDateの振る舞いを変更することでテストを書きやすくするライブラリNSDate+SRGFekableの紹介でした! もし使えそうな場面があれば、ご利用ください。

24
24
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
24