Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

入力時にバリデーションを行い、単体テストを書く #7

Open
ginrou opened this issue Jun 1, 2015 · 3 comments
Open

Comments

@ginrou
Copy link
Contributor

ginrou commented Jun 1, 2015

TODOアプリを作ってみようシリーズの最終回の演習課題です。

内容

TODO入力時のバリデーションと単体テストを取り扱います。

todo7
↑バリデーションの様子

todo8
↑テスト実行の様子. この例では失敗してます。

目的

  • 入力値のバリデーションを行えるようにする
  • UIAlertControllerを表示する
  • 単体テストが書けるようになる

スタート地点

前回( #6 )のゴール地点であるブランチlocal-notification からスタートしてください。

ゴール地点

ブランチ unit-testを見ると解答例が書いてあります。

教材

アプリの仕様

  • TODOを入力する時に以下のバリデーション(入力値検査)を行うようにしてください
    • TODOの本文がきちんと含まれていること (ゼロ文字や空白のみはダメ)
    • 締め切り時間が現在より先に設定されていること (過去の締め切りはダメ)
  • 入力値が不正な場合は、渓谷するアラートを表示する
  • 入力値検査に関する単体テストを書く

実装の方針

TODO追加時にバリデーションを行い、不十分な場合はアラートを表示する 4a49faf

まず、入力されたTODOが仕様を満たすかどうかをチェックするメソッドを作ります。
仕様を満たす場合は YES を、満たさない場合は NO を返すようにします。

そしてTODOが正しくない場合はアラートを表示します。
アラートの表示にはUIAlertControllerを用います。使い方は差分のコードを見てください。
リファレンスは UIAlertController Class Reference になります。

このUIAlertControllerはiOS8でクラス名が変更になったクラスで、以前はUIAlertViewを利用していました。

単体テストを追加する b4eb18b

次に単体テストを追加していきます。テストフレームワークであるXCTestがプロジェクト作成時に導入されているのでそれを利用します。
今回テストを行いたい対象はAddTodoViewControllerなのでAddTodoViewController.mのターゲットにTodoTestsを追加します。

2015-06-01 3 57 40 pm

次に新規テストファイルを追加します。NewFileからテンプレートは"Test Case Class"を選択し、subclass of はXCTestCaseを選択します。
ファイル名は特に制約はありませんがAddTodoViewControllerTestsなどにすると分かりやすいです。

テストファイルが追加できたらテストターゲットのヘッダファイルをimportしてテストを記述します。
XCTestによるテストの記述方法は教材を参照してください。
テストには、成功するパターンや失敗するパターン、異常な入力などの様々なパターンを網羅するとより安心して開発に望むことができます。
ちなみに 4a49faf では以下のケースに対するケアが漏れていて、テストを実行すると失敗となります。

  • todoの本文が空白文字のみ
  • 締め切り日時にnilを渡したケース
@ginrou
Copy link
Contributor Author

ginrou commented Jun 4, 2015

テストの実行は

  • メニューのProduct → Run
  • ⌘ + U

で実行できます。

@ginrou
Copy link
Contributor Author

ginrou commented Jun 4, 2015

以下のテストケースを実装してみてください

    // 順正常系 : NOが返るパターン
    // 1. titleが空文字列の場合
    // 2. titleがキーとして存在しない場合
    // 3. titleが空白のみの場合
    // 4. dateが昔のパターン
    // 5. dateがキーとして存在しない場合パターン
    // 6. 引数にnilを渡したパターン
    // 7. dateの型がNSStringの場合
    // 8. 余分なkey-valueペアがある場合 : 成功するパターン

@ginrou
Copy link
Contributor Author

ginrou commented Jun 4, 2015

テストケース解答例

    // 正常系
    todo = @{@"title": title, @"date": date};

    XCTAssertTrue([vc isValidToDo:todo]);

    // 順正常系 : NOが返るパターン
    // 1. titleが空文字列の場合
    todo = @{@"title": @"", @"date": date};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 2. titleがキーとして存在しない場合
    todo = @{@"date": date};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 3. titleが空白のみの場合
    todo = @{@"title": @"     ", @"date": date};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 4. dateが昔のパターン
    todo = @{@"title": title, @"date": [NSDate dateWithTimeIntervalSinceNow:-100]};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 5. dateがキーとして存在しない場合パターン
    todo = @{@"title": title};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 6. 引数にnilを渡したパターン
    XCTAssertFalse([vc isValidToDo:nil]);

    // 7. dateの型がNSStringの場合
    todo = @{@"title": title, @"date": @"hogehoge"};
    XCTAssertFalse([vc isValidToDo:todo]);

    // 8. 余分なkey-valueペアがある場合 : 成功するパターン
    todo = @{@"title": title, @"date": date, @"hoge": @(10)};
    XCTAssertTrue([vc isValidToDo:todo]);

メソッドisValidTodo: も以下のようになります

- (BOOL)isValidToDo:(NSDictionary *)todo
{
    NSString *title = todo[@"title"];
    NSDate *date = todo[@"date"];

    NSString *trimedTitle = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if (date == nil) return NO;
    if ([date isKindOfClass:[NSDate class]] == NO) return NO;

    if (trimedTitle.length == 0) return NO;
    if ([date timeIntervalSinceNow] < 0.0) return NO;

    return YES;
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant