応用TDD:ReactNative実戦編

40258 ワード

いわゆるTDD


[テストドライバ開発]バー


ソフトウェア開発方法論の1つとして、開発自体は「テスト」が主導している.製品の機能を実現するために、まずテストコードを作成し、その機能が正常に動作しているかどうかを検証する.

TDD開発段階

  • ReadMe Driven Development:要求技術
  • Make it RED(失敗):テストコード作成
  • Make it GREEN(成功):実際のコード
  • を記述
  • Make it BLUE(リビルド):最低限のコードリビルド
  • なぜTDDを使うのですか?


    従来の開発方式を採用すれば、消費者の需要は最初から明確ではない可能性があるため、最初から完璧な設計を行うことは難しい.また、再設計とコード書き換えを繰り返すと、製品の品質が低下します.小さな機能変更でも、すべての部分を再テストする必要があり、テストコストが増加します.
    TDDはテストコードと製品コードを機能別に記述するため,低依存性と低依存性のモジュール化方式で開発する.これにより、再設計、デバッグ時間が短縮され、さらなる実装が容易になります.(特に既存のコードへの影響を最小限に抑えることができる)

    リポジトリのインストールと紹介


    JEST


    Facebookで作成したJavaScriptテストライブラリです.react-native-cliを使用してプロジェクトを構成する場合、dev依存項目に含まれています.
  • descripe():API
  • は、複数のテストコードを1つのテストユニットに組み合わせる
  • test():テストコードのAPIを返します.
  • テストケースを示します
  • expect():API
  • 、テスト対象のターゲット(期待値)を含む
  • beforeEach():各テストコードの前に繰り返し実行される論理を含むAPI
  • Enzyme


    Airbe&Bによって作成されたReact構成部品テストライブラリです.酵素、酵素-アダプタ-反応、反応-domを同時に設定します.
  • 浅い:単純な構成部品をメモリにレンダリング(単一の構成部品をテスト)
  • mount:HOCまたはサブエレメントはすべてレンダリング(他のエレメントとの関係をテスト)
  • レンダリング:構成部品を静的htmlとしてレンダリング

    Detox


    ユーザの観点からEnd to Endのライブラリをテストします.Android/iosはサポートされていますが、まずiosでテストしただけです.リンゴソース、デトックス-cli、デトックス、jest-marchを同時に設定します.
  • は、ユーザの観点からテストするasync機能
  • を実際に提供する.

    ToDoListテストコードの作成


    構成部品をレンダリングしpropsを確認する


    以下の簡単なToDoリストのテストコードを作成します!

    まず、アプリケーションを初めて実行するときに、画面タイトルに使用される「TODOTDD」コンテンツが以下のテキストコンポーネントに含まれているかどうか、およびAddToDoおよびToDoListが存在するかどうかを記述することができる.
    describe('App', () => {
      const wrapper = shallow(<App></App>);
      test('snapshot test', () => {
        expect(wrapper).toMatchSnapshot();
      });
      it('is Text visible?', () => {
        expect(wrapper.find('Text').contains('ToDo TDD')).toBe(true);
      });
      it('is AddToDO visible?', () => {
        expect(wrapper.find('AddToDo')).toHaveLength(1);
      });
      it('is ToDoList visibla?', () => {
        expect(wrapper.find('ToDoList')).toHaveLength(1);
      });
    });
    AddToDoコンポーネントはTextInputとボタンで構成されており、ボタンをクリックするとTextInput内部のOnAdded関数が呼び出されます.内部に対応するコンポーネントがあるかどうかを確認する操作は、上記のコードと類似しているため、関数呼び出し関連部分の作成方法は次のとおりです.テストを行う前に、AddToDoをレンダリングし、TextInputコンテンツを作成し、ボタンを押します.次にitでOnAdded関数が呼び出されたかどうか、テキストコンテンツがあるかどうかを確認します.
    describe('AddToDo Interaction', () => {
      let wrapper;
      let props;
      const text = 'Add To Do Item';
    
      beforeEach(() => {
        props = {
          onAdded: jest.fn(),
        };
        wrapper = shallow(<AddToDo {...props}></AddToDo>);
        wrapper.find('TextInput').simulate('changeText', text);
        wrapper.find('Button').prop('onPress')();
      });
    
      it('should call the onAdded callback with input text', () => {
        expect(props.onAdded).toHaveBeenCalledTimes(1);
        expect(props.onAdded).toHaveBeenCalledWith(text);
      });
    });
    ToDonderItemのテストコードを記述するのも似ています.デフォルトでは、テキスト、Buttonの内部位置が適切かどうか、初期スタイルはどうか、propsが特定のデータを入力した場合、どのような異なるスタイルがあるかを決定します.その後、各ボタンのクリックに伴い、対応する関数が正しく呼び出されたか否かを決定することができる.
    // component rendering
    describe('rendering', () => {
      let warpper;
      let props;
    
      beforeEach(() => {
        props = {
          item: {},
        };
        wrapper = shallow(<ToDoRenderItem {...props}></ToDoRenderItem>);
      });
    
      it('should render a Text', () => {
        expect(wrapper.find('Text')).toHaveLength(1);
      });
    
      it('should render two buttons', () => {
        expect(wrapper.find('Button')).toHaveLength(2);
      });
    
      describe('Uncompleted', () => {
        it('should have the default style', () => {
          expect(wrapper.prop('style')).toBe(styles.default);
        });
      });
    
      describe('Completed', () => {
        beforeEach(() => {
          props.item.completed = true;
          wrapper = shallow(<ToDoRenderItem {...props}></ToDoRenderItem>);
        });
        it('should have the completed style', () => {
          expect(wrapper.prop('style')).toBe(styles.completed);
        });
      });
    });
    
    // interaction
    describe('interaction', () => {
      let wrapper;
      let props;
      beforeEach(() => {
        props = {
          item: {text: 'first ToDo', completed: false},
          index: 0,
          onCompleted: jest.fn(),
          onDeleted: jest.fn(),
        };
    
        wrapper = shallow(<ToDoRenderItem {...props}></ToDoRenderItem>);
      });
    
      it('Complete feature', () => {
        wrapper.find('Button').at(0).prop('onPress')();
        expect(props.onCompleted).toHaveBeenCalledTimes(1);
        expect(props.onCompleted).toHaveBeenCalledWith(props.index);
      });
    
      it('Delete feature', () => {
        wrapper.find('Button').at(1).prop('onPress')();
        expect(props.onDeleted).toHaveBeenCalledTimes(1);
        expect(props.onDeleted).toHaveBeenCalledWith(props.index);
      });
    });
    ワークショップで議論している間に、これらのコードをすべて1つのファイルに書くべきかどうかを聞かれ、sangtestコマンドを実行すると、フォルダ内のすべてのテストコードファイルが実行され、確認され、コンポーネントと機能に応じて適切に区分すればよい.
    ファイルを初めて実行すると、すべてのテストに失敗が表示されます.これは前述のREDステージです!テストコードを通過したGREENフェーズが実行できるようになりました.(BLUEフェーズが以降のテストコードに関係のないフェーズであることを忘れないでください)作成したコードがすべての対応するテストコードを通過できる場合、糸テストコマンドで実行すると、以下の結果が表示されます.

    E 2 Eの動作をDetoxでチェック


    前のように構成部品のレンダリングまたは内部データを表示できますが、ユーザーが実際にサービスを使用するすべてのスキームが正常に動作するようにするにはどうすればいいですか?DetoxのEnd To Endテスト機能を使用して、ToDonderItemを直接追加/完了または削除するプロセスが正常であることを確認します.
    追加と完了
  • :ユーザーは入力フィールドに内容を入力し、「追加」ボタンをクリックし、完了していないToDonderItemが正常に追加されていることを確認します.「完了」ボタンをクリックし、プロジェクトが「完了」状態に切り替わったことを確認します.
  • 削除
  • :前のように入力フィールドに内容を入力し、追加ボタンをクリックしてリストに追加するかどうかを確認します.[後で削除](Remove After)ボタンをクリックすると、リストに項目が削除されず、表示されないことを確認します.
  • describe('Interaction Test', () => {
      beforeAll(async () => { // 처음에 앱을 실행하고
        await device.launchApp();
      });
      beforeEach(async () => { // 매 테스트마다 리로드를 통해 혹시 모를 영향을 끼칠 요소들을 방지하자
        await device.reloadReactNative();
      });
    
      it('Completing ToDo Item should work!', async () => {
        const text = '입력 완료 여부 체크';
        await element(by.id('textInput')).tap();
        await element(by.id('textInput')).typeText(text);
        await element(by.id('addButton')).tap();
        await element(by.id('completeButton')).multiTap(1);
        await expect(
          element(
           // toDoList라는 아이템 내에 '입력 완료 여부 체크'라는 내용을 
           // 가진 텍스트가 있는지 확인하고, 
           // 그 아이디가 현재 testId로 'completed'를 가지고있는지 확인한다.
            by.id('completed').and(by.text(text)).withAncestor(by.id('toDoList')),
          ),
        ).toBeVisible(); 
      });
    
      it('Deleting ToDo Item should work!', async () => {
        const text = '입력 삭제 여부 체크';
        await element(by.id('textInput')).tap();
        await element(by.id('textInput')).typeText(text);
        await element(by.id('addButton')).tap();
        await element(by.id('deleteButton')).multiTap(1);
        await expect(
          element(by.text(text).withAncestor(by.id('toDoList'))),
        ).not.toBeVisible(); // not toBeVisible
      });
    });
    deuto build-ciosで構築し、deuto test-c iosでテストし、以下の結果を確認できます.テストコマンドを実行するときは、以下のように、実際のアプリケーションを実行しながら、ユーザーがテキストを入力したり、ボタンをクリックしたりして、目でシーンの実行状況をテストすることができます.

    実行中に端末をチェックすると、[OK]文が追加され、テストの実行に伴って正常に実行されたことを示し、テストが成功/失敗したかどうかを詳細に説明します.QA時間を大幅に短縮することができ、個人的にはとても満足しています.

    途中でコードを交換した後、再ロードされて自動的にコードに反映されるべきだと思いましたが、ワークショップでは継続していなかったので、いっそ構築内容をキャンセルして確認したところ、正常に行われました.△さらにxcode clean buildのためにコマンドを解放し、再構築したが、そのコマンドは起動しなかった...xcodeで構築されたのではなく、私は何を言っているのだろうか.ダメならデトックス公式文書で言うとおりにしましょう…!

    TDDを使う必要はありません。


    もちろん、すべてのプロジェクトや他のすべての状況でTDDを使用する必要はありません.TDDを初めて導入する場合、初期コストはTDDを使用しない場合よりも大きくなります(テストコードの作成).また、TDDはエラーを事前に発見させるだけです.エラーの「解決」は最終的に開発者が担当することを忘れてはいけません.また、テストコードを繰り返すだけで、これらのコードを意味なく使用することは、ビジネスプロセスの増加につながるだけです.TDDは、共通の目標を効率的に実現するための「ツール」です.羅、忘れないで、よく使って結局私達の手の中で掌握します