角度のある世界におけるテスト駆動開発


待って!TDDとは


だから、あなたはこのTDDのことについて聞いて、それを使用して起動したいが、理由がわからない.心配しないでください、インターネット救助に!しかし、まず、TDDは何ですか?
TDDはテストドリブン開発を表し、コードを書く方法です.これは、最初にテストを書くということを意味します.これは赤緑のアプローチとも呼ばれます.長い話短い、それはテストによって駆動される開発です.
小さな例:2つの数字を追加するメソッドを書きたいとしましょう.さて、メソッドを実装する代わりに、テストを最初に書きます.
it('should add the values', () => {
  const result = addValues(2, 4);

  expect(result).toBe(6);
});

let addValues = (value1, value2) {}
さて、テストを実行すると失敗します.

現在のところ、何が問題なのかわかります.
it('should add the values', () => {
  const result = addValues(2, 4);

  expect(result).toBe(6);
});

let addValues = (value1, value2) {
  return value1 + value2;
}
そして今テストはパスします.

今、それはかなり満足している、右?
したがって、これはいくつかあります.
  • あなたのコードがテストされていることを確認しますので、あなたが書いたすべてのコードについてのスプリント書き込みの最後の2日間を費やす必要はありません
  • は、実用的な方法でコードを書くことを確認します.Yagni(あなたはそれを必要としない)というものがあります.そして、それは考えて、気候変動と世界飢餓を解決しようとしている次のコードの上で開発者についてあります.言うまでもなく、最も頻繁に言うと、これはコードを任意の本当の利益なしで複雑になります.
  • このように機能を書き終えたとき、すべてのテストがパスしているのを見て、テストがまだパスしているときにリファクタリングしたとき、あなたは何も壊しませんでした.どのくらいの頻度であなたの単純なプル要求で製品を壊すのが怖かったですか?このような感じ:
  • 最後だが、少なくとも、それは色についてのすべてです!色を見ることは私達を幸せにする.何かが失敗して通過することを見て、私たちも幸せになります.
  • 3ヶ月と500のテスト後、我々は我々が構築している製品に自信の本当の意味を持っている.
    ここのキーワードは

    今、角度と我々のコンポーネントに戻る


    だから、今、あなたはTDDは何についてのアイデアがあります.次の停止は、コンポーネントで使用します.このポストのために、私はあなたに角度の基本的な理解があると仮定します.
    私はテストのための冗談を使用しますが、原則は同じです.
    我々の例については、映画でリストを作成できる架空のアプリケーションを使用してみましょう.今、空間と時間のどこかに、我々はまた、ユーザーが彼/彼女の好きな映画を見ることができるページが欲しいと決めます.だから、さらにADOなしで、それを構築しましょう!
    まず、コンポーネントを生成します.
    ng g c favorite-movies
    
    さて、これはコンポーネントを生成します.では、まず何をしたいですか?我々は好きな映画のようなタイトルを追加します.しましょう!
    WAAAAIT!何を議論しましたか.テストを書いて、それを修正するコードを書きます.
    だから、それはどのように見えるでしょうか?このような
      describe('Render', () => {
        beforeEach(() => {
          fixture.detectChanges();
        });
    
        it('should have a title', () => {
          const titleElements = fixture.debugElement.queryAll(By.css('h1'));
          expect(titleElements.length).toBe(1);
          expect(titleElements[0].nativeElement.innerHTML).toBe('Favorite movies');
        });
      });
    
    私たちはレンダリングのための新しい記述ブロックを作成しました、そして、そこで、我々はH 1タグがある、そして、それが正しい内容を持っていることをチェックする新しいテストを加えました.
    今、我々はテストを実行し、大きな驚き、それは失敗!フレットしないでください.
    <h1>Favorite movies</h1>
    
    我々はHTMLテンプレートにこれを追加し、今テストを通過!
    おめでとう!TDDウェイでコードの最初のビットを書きました.背中に自分自身を軽く叩くためにいくつかの時間をください.グッドジョブ!今すぐ停止します.もっと仕事がある.
    次に、私たちは、映画のリストを与えられて、HTMLで示されることをテストしたいです.では、どうしますか?私たちはそれのためのテストを書く!
    
    const favoriteMoviesToUse: Movie[] = [
      { title: 'Interstellar' } as Movie,
      { title: 'The big Lebowski' } as Movie,
      { title: 'Fences' } as Movie
    ];
    
    describe('FavoriteMoviesComponent', () => {
      beforeEach(() => {
        fixture = TestBed.createComponent(FavoriteMoviesComponent);
        component = fixture.componentInstance;
        component.favoriteMovies = favoriteMoviesToUse;
      });
    
      describe('Render', () => {
    
        it('show all the favorite movies', () => {
          const movieElements = fixture.debugElement.queryAll(By.css('.movie'));
          expect(movieElements.length).toBe(favoriteMoviesToUse.length);
        });
    
         it('should show the movie titles', () => {
          const movieElements = fixture.debugElement.queryAll(By.css('.movie'));
          movieElements.forEach((movieElement: DebugElement, index) => {
             expect(movieElement.nativeElement.innerHTML).toContain(favoriteMoviesToUse[index].title);
          });
        });
      });
    });
    
    ここでは、コンポーネントに新しい@ input ()プロパティを追加し、新しいリストを作成し、コンポーネントへの入力として渡しました.次に、レンダリングされたHTMLがクラスムービーで正しい量の要素を含むことをテストしています.
    もちろん、このテストは失敗します.それではパスしましょう.
    <div class="movie" *ngFor="let movie of favoriteMovies">
      {{ movie.title }}
    </div>
    
    これは私たちのテンプレートに追加されます.うん!テストは今パス!
    neeeeext!
    データが実際に非同期的にサービスから来るならば、どうですか?
    テストを調整しましょう
    describe('FavoriteMoviesComponent', () => {
      let component: FavoriteMoviesComponent;
      let fixture: ComponentFixture<FavoriteMoviesComponent>;
      let favoriteMovieService: FavoriteMoviesService;
    
      beforeEach(() => {
        fixture = TestBed.createComponent(FavoriteMoviesComponent);
        component = fixture.componentInstance;
        favoriteMovieService = TestBed.get(FavoriteMoviesService);
        jest.spyOn(favoriteMovieService, 'getFavoriteMovies').mockReturnValue(of(favoriteMoviesToUse));
      });
    
      describe('Getting the movies', () => {
        it('should get the movies from the service', () => {
          fixture.detectChanges();
          expect(favoriteMovieService.getFavoriteMovies).toHaveBeenCalled();
        });
      });
    });
    
    現在、我々のテストは、サービスが注入されると仮定します.私たちがすることは応答を模擬して、サービスが呼ばれるのをチェックします.テストは失敗し、それらを修正しましょう!
    export class FavoriteMoviesComponent implements OnInit {
      favoriteMovies$: Observable<Movie[]>;
      constructor(private favoriteMovieService: FavoriteMoviesService) {}
    
      ngOnInit() {
        this.favoriteMovies$ = this.favoriteMovieService.getFavoriteMovies();
      }
    }
    
    @ input ()を使用する代わりに、コンポーネントを使用します.最後のパズルピースはテンプレートです.
    <ng-container *ngIf="(favoriteMovies$ | async); let favoriteMovies">
      <div class="movie" *ngFor="let movie of favoriteMovies">
        {{ movie.title }}
      </div>
    </ng-container>
    
    したがって、現在サービスからデータを取得し、それをレンダリングする完全に動作するコンポーネントがあります.他に何が必要ですか?何もない!家に帰って、いくつかの赤い死償還2をする時間🐴.
    waaaaait!エラーがある場合は?
    OK , OK ,何か失敗した場合には適切に扱われるようにいくつかのテストを書きましょう.
        it('should show an error if getting the movies fail', () => {
          const errorToThrow = 'User not found';
          jest
            .spyOn(favoriteMovieService, 'getFavoriteMovies')
            .mockReturnValue(throwError(errorToThrow));
    
          fixture.detectChanges();
    
          const errorElement = fixture.debugElement.queryAll(By.css('.error'));
          expect(errorElement.length).toBe(1);
          expect(errorElement[0].nativeElement.innerHTML).toContain(errorToThrow);
        });
    
        it('should not show an error if getting the movies succeeds', () => {
          fixture.detectChanges();
    
          const errorElement = fixture.debugElement.queryAll(By.css('.error'));
          expect(errorElement.length).toBe(0);
        });
    
    だから、私たちは、エラーが表示される場合は、お気に入りの映画を取得する場合は、失敗し、すべての計画に従って行く場合隠されていることを確認します.
    必要なのは、コンポーネントのエラーをキャッチすることです.
      ngOnInit() {
        this.favoriteMovies$ = this.favoriteMovieService.getFavoriteMovies().pipe(
          catchError((error: any) => {
            this.error = error;
    
            return of([]);
          })
        );
      }
    
    テンプレートのエラーを表示するには、次の手順に従います.
    <div class="error" *ngIf="error">
      {{ error }}
    </div>
    
    今、すべてのハードワークの後、我々はデータを取得し、エラーを処理し、我々がそれを期待するすべてをレンダリングする非同期サービスを使用して完全に動作するコンポーネントを持っている.
    今、いくつかの目のお菓子のための時間:

    かなりクールな右?
    さて、これが動作するためには、サービスを実装する必要はありません.それはちょうど存在しなければならなくて、GetFavoritemoviesと呼ばれているメソッドを持っていなければなりません.

    包む


    私は知っている、私は知っている、たくさんの単語.私は重要な情報を省略することなく私はできるだけ簡潔にしようとしました.口は重宝手は宝.これは私の最初の記事です.うまくいけば、次の1つでは、アングルサービスのテストについて話します.もちろんTDD.
    このプログラムを楽しんでください📺. 次のいずれかを参照してください!