iOSユニットのテストはどう書きますか?

6218 ワード

ユニットテストとは?


プログラムモジュール(ソフトウェア設計の最小単位)に対して正確性検査のテスト作業を行う.プログラムユニットは、アプリケーションの最小テスト可能な部品です.オブジェクト向けプログラミングの場合、最小ユニットはiOSが独自のテストフレームワークOCUnitとUItestsを統合した方法です.

ユニットテストはなぜですか?


ユニットテストを実行するのは、このコードの動作が私たちの期待と一致していることを証明するためです.例えば、いくつかの機能が正常かどうか、インタフェースが正常かどうか、特にいくつかの大きなプロジェクトで、プログラムが誤って変更されたり、新しい問題を引き起こしたりすることを防止します.ユニットテストにはいくつかのメリットがあります.
  • プログラマーに協力してBUGの具体的な位置をできるだけ早く見つける
  • プログラマーに自分のプログラムに自信を持たせる
  • は、プログラマがプロジェクトをコミットする前にコードをより丈夫にすることができる
  • .
  • プログラマーのより良い開発に協力できる
  • 他のプログラマーにプログラムが
  • を呼び出す方法を示すことができます.
  • プロジェクト責任者にシステムの現在の状況をより理解させることができ、その意味に疑問がある場合は、この文章
  • を参照してください.

    ユニットテストはどう書きますか?


    私はユニットテストを書いたばかりで、ネット上にたくさんの文章を見たが、まだ手がつけられない.本文はいくつかの点をまとめ,いくつかの例を抜粋した.
  • **テスト**
  • プロジェクトの共通クラスにおける開示方法は、共通コンポーネントをユニットテストに組み込むことで、下位レベルの動作の正確性と丈夫性を大幅に強化することができる.
  • ネットワークデータ層方法のテストでは、データインタフェースは一般的にあまり多くありません.ここでのテストはインタフェースの正常さを保証することができます.
  • ビジネスロジック層テストは、ビジネスロジックを正しく保つことができ、製品がリリースされた後、ビジネスロジックのユニットテストを通じてBUGを直接見つけることができます.
  • バグを修正する時テスト、もしユーザーがバグがあることを反映するならば、先にテストを書いてバグを再現することができて、それから問題を見つけてバグを修復して、テストは検証して、最後にテストを保留して、
  • よりテストしやすいコードは多くの場合、プロジェクトでは複数のクラス間のインタラクティブな処理が避けられず、結合度が高く、この操作は非常にデバッグしにくい.ユニットテストの原則の一つは、テストに使用するコード要求機能が単一であることであり、これは実際には良好なコード設計の考え方と非常に一致している.一方、良好なコード構造設計は、テスト例の構築をより迅速かつ簡単にすることができます.逆にユニットテストは,クラス間の結合を低減し,テストの干渉を低減し,排除する方法を考えさせた.いずれにしても、より良い開発者になりたい場合は、ユニットテストはコード認知を迅速に向上させる重要な手段の一つです.
  • オーバーライド率ユニットが書くのが合理的かどうか、または要求に達したかどうかに注目する唯一の基準は、テスト全体のコードオーバーライド率である.コードオーバーライド率は、コードが実行される実際のプログラムパスをテストするオーバーライド率です.実際のプログラムでは多くのループや判断などの分岐経路がある可能性がある.良いユニットテストは、可能なすべてのパスをたどることができるはずです.これにより、ほとんどの状況がテストされたことを保証することができます.
  • 境界条件データ、例えば値タイプデータの最大値、最小値、DBNull、または方法で使用される条件境界、例えばa>100では100がこのデータの境界となる.また,試験の際には境界を超えたデータを試験条件として試験しなければならない.
  • は空のデータであり、一般的な空のデータは参照タイプのデータ、すなわちNull値に対応する.
  • **フォーマットが正しくないデータ**は、参照タイプのデータまたは構造オブジェクトに対して、タイプは正しいが内部のデータ構造が正しくないデータです.たとえば、データベース・エンティティ・オブジェクトでは、属性が空でない必要がありますが、この場合は空に属することができます.これにより、このオブジェクトは不正なデータベースに属します.


  • ユニットテストコードを作成するときに、テスト方法で使用される可能性のある外部データを理解してから、これらの外部データを上記のように一度に設定してから、方法を実行します.これにより、外部データのすべての状況が正しくテストされるようになります.

    抜粋したいくつかの例


    **例1**論理テストの操作手順の前後で、対応するデータが変更されたはずです.そうすれば、テストを容易にすることができます.
    @interface LXDTestsModel : NSObject
     
    @property (nonatomic, readonly, copy) NSString * name;
    @property (nonatomic, readonly, strong) NSNumber * age;
    @property (nonatomic, readonly, assign) NSUInteger flags;
     
    + (instancetype)modelWithName: (NSString *)name age: (NSNumber *)age flags: (NSUInteger)flags;
     
    - (instancetype)initWithDictionary: (NSDictionary *)dict;
    - (NSDictionary *)modelToDictionary;
     
    @end
    

    テスト例では、モデルとjson間の変換が正しいかどうかをテストするためのtestModelConvertメソッドを定義しました.
    - (void)testModelConvert
    {
        NSString * json = @"{\"name\":\"SindriLin\",\"age\":22,\"flags\":987654321}";
        NSMutableDictionary * dict = [[NSJSONSerialization JSONObjectWithData: [json dataUsingEncoding: NSUTF8StringEncoding] options: kNilOptions error: nil] mutableCopy];
     
        LXDTestsModel * model = [[LXDTestsModel alloc] initWithDictionary: dict];
        XCTAssertNotNil(model);
        XCTAssertTrue([model.name isEqualToString: @"SindriLin"]);
        XCTAssertTrue([model.age isEqual: @(22)]);
        XCTAssertEqual(model.flags, 987654321);
        XCTAssertTrue([model isKindOfClass: [LXDTestsModel class]]);
     
        model = [LXDTestsModel modelWithName: @"Tessie" age: dict[@"age"] flags: 562525];
        XCTAssertNotNil(model);
        XCTAssertTrue([model.name isEqualToString: @"Tessie"]);
        XCTAssertTrue([model.age isEqual: dict[@"age"]]);
        XCTAssertEqual(model.flags, 562525);
     
        NSDictionary * modelJSON = [model modelToDictionary];
        XCTAssertTrue([modelJSON isEqual: dict] == NO);
     
        dict[@"name"] = @"Tessie";
        dict[@"flags"] = @(562525);
        XCTAssertTrue([modelJSON isEqual: dict]);
    }
    

    **例2**ユニットテストはメインスレッドで行われるため、非同期操作のテストは実行が完了する前に終了することが多い.解決方法は二つある
  • while()方式無限ループ待ち
  • //waitForExpectationsWithTimeout , 。
    #define WAIT do {
     [self expectationForNotification:@"RSBaseTest" object:nil handler:nil];
     [self waitForExpectationsWithTimeout:30 handler:nil];
    } while (0)
    #define NOTIFY [[NSNotificationCenter defaultCenter]postNotificationName:@"RSBaseTest" object:nil]
    
    -(void)testRequest{
        // 1. 
        AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
        mgr.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",nil];
    
        // 2. GET 
        [mgr GET:@"http://www.weather.com.cn/adat/sk/101110101.html" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
            NSLog(@"responseObject:%@",responseObject);
            XCTAssertNotNil(responseObject, @" ");
            NOTIFY // 
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"error:%@",error);
            XCTAssertNil(error, @" ");
            NOTIFY // 
        }];
        WAIT  // 
    }
    
  • 別の非同期試験は、GCD信号量
  • を用いることを実現する.
    - (void)downloadImageURLWithString:(NSString *)URLString
    { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        NSURL *url = [NSURL URLWithString:URLString];
        __unused Photo *photo = [[Photo alloc] initwithURL:url
                                 withCompletionBlock:^(UIImage *image, NSError *error) {
                                     if (error) {
                                         XCTFail(@"%@ failed. %@", URLString, error);
                                     }
    
                                     dispatch_semaphore_signal(semaphore);
                                 }];
    
        dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, kDefaultTimeoutLengthInNanoSeconds);
        if (dispatch_semaphore_wait(semaphore, timeoutTime)) {
            XCTFail(@"%@ timed out", URLString);
        }
    }
    

    **オープンソースプロジェクト**でよく使われる第三者フレームワーク、例えばYYModel、AFNetworking、Alamofireなどの優れたフレームワークの中にも、フレームワーク自体に対して作成されたユニットテストがあり、これらのユニットテストを模倣することを学ぶことも、自分を迅速に向上させる手段です.