Ionicテストの自動化テストの概念と実践
8849 ワード
Testing Concetps
外国の有料のionic学習サイトに翻訳すると、元の住所が貼れなくなります.
自動化テストは長い間現れ、テストに関する概念と最高の実践がたくさんあります.特定の言語はtestsを記述するために使用されますが、同じ概念はあなたが使用しているプログラミング言語で使用することができます.
AAA: Arrange, Act, Assert
これはあなたが理解する必要がある重点概念かもしれませんが、テストの作成を指導する原則です.テストコードを作成するには、次の手順に従います.
この概念をどのように運用するかを見てみましょう.
// Test that `incrementTotal()` increases the total by 1
/* Arrange */
let myTestObject = new SomeObject();
/* Act */
let oldTotal = myTestObject.getTotal();
myTestObject.incrementTotal();
let newTotal = myTestObject.getTotal();
/* Assert */
if(newTotal === oldTotal + 1){
console.log("test passed!");
} else {
console.log("test failed!");
}
まず、arrangeテスト--テストするobjectを設定し、act-objectを呼び出す方法であり、最後にassert-
newTotal
はoldTotal+1
に等しい.後で、私たちのテストはもっと複雑になり、vanilla JavaScriptで似たようなコードを書くことはありません.しかし、Arrange,Act,Assertという概念はまだ使われています.
One Assertion Per Test
テストを作成する場合、1つのtestには通常1つのassertionしかありません.testは特定のことをテストする必要があります.もちろん、これは強制的ではありません.自分の考え通りに簡単に1つのtestの中でN以上のassertionをすることができます.しかし、一般的にはこれは良い考えではありません.
このルールに違反するtestを見てみましょう.
it('should allow todos to be modified', () =>{
// trigger edit todo functionality
// expect that the edit page was launched with the todo
// trigger delete todo functionality
// expect that the todo was passed to the delete function in the data provider
});
todos
が変更できるかどうかをテストします.実際には、todos
が修正可能かどうか、todos
が削除可能かどうかの2つのことがtestされます.どうしてこれは良い構造ではないと言ったのですか.should allow todos to be modified
という説明はあまりにも広くて、testが何なのか一目ではわかりません.良い構造は次のようになります.
it('delete function should pass the todo to the deleteTodo method in the provider', () => {
// trigger delete function on the page
// check that the deleteTodo method on the provider has been called
});
it('edit function should launch the EditTodo page and pass in the todo as a parameter', () => {
// trigger edit function on the page
// check that the navCtrl pushed the EditTodo page with the `todo` as a parameter
});
この2つのtest定義はよりよく、独立して実行されます.testの説明が長すぎることを心配する必要はありません.かえって長ければ長いほどいいです.結局、あなたの考えの改善と維持に役立ちます.
Make Tests Independent
前の例ではtestsが互いに独立している概念が概ね理解されている.前の例では、同じtestで2つのテストを実行したため、最初のテストは2番目のテストの動作を変更する可能性があります.
タイムリーに同じtestで複数のテストを走っていないと、上記の問題に直面します.例:
let groceryList = new GroceryList();
it('we should be able to push items to the grocery list', () => {
// push an item to the grocery list
// check that it was added
});
it('the grocery list should be empty by default', () => {
// check that the grocery list is empty
});
この場合、最初のテストは2番目のテストに干渉します.grocery listのデフォルトは空ですが、最初のテストにitemを追加するとデフォルトではありません.この問題を解決するには、各テストを実行する前にresetを保証する必要があります.たとえば、次のようにします.
let groceryList;
beforeEach(() => {
groceryList = new GroceryList();
});
it('we should be able to push items to the grocery list', () => {
// push an item to the grocery list
// check that it was added
});
it('the grocery list should be empty by default', () => {
// check that the grocery list is empty
});
各試験方法の前に
beforeEach
方法を追加した.これは後述するIsolate Unit Tests
one assertion per test
の例では、deleteTodo
メソッドの呼び出しをテストした.todo
が本当に削除されたかどうかをテストしていません.この場合、
todo
の削除操作は、現在のpageの動作ではなく、todos
のproviderを処理する.現在のpageはこのproviderに情報を伝えるだけで、これが私たちのテストの内容です.deletionプロセスが正しいことをテストするためにproviderに関連する独立したテストが必要です.1つのユニットテストでは、テストするコードは、APPの他の部分と完全に関係を断ち切る必要があります.dataを送信することはできません.serverのdataを引き継ぐことはできません.サーバにdataを送信することはできません.他の外部(テスト中ではありません)呼び出しもできません.
テストの機能がserverリクエストに依存している場合、またはNavParamsを介して渡されるパラメータなどのデータを渡す必要がある場合は、二重テスト
データを使用します.基本的な考え方は、NavParamsからデータを抽出してテストを実行する必要がある場合、実際にはNavParamsからデータを抽出する必要はありません.独自の偽実装NavParamsを使用してテストデータを転送することができます.サーバから応答を受信する必要がある場合は、実際のサーバに要求を発行しないで、要求をブロックし、自分の偽データを使用して応答する必要があります.最初はこれらを把握するのが難しいので、アプリが動作しているかどうかをテストしたいと思っています.なぜリクエストサーバを迂回して、私たちが知っている正しいdataに戻りますか?テストと他のコントロール、サービスの統合はユニットテストの一部ではありません.ユニットテストに興味があるのはunitそのものだけです.ユニットテストは、独立したコードブロックをテストすることについてです.
Don't Try to Test Everything
想像してみてください.私たちのユニットは1つの数が偶数かどうかを教えてくれる方法をテストします.テストはこうです.
it('isEven function should return true for even numbers', () => {
expect(isEven(4)).toBe(true);
});
it('isEven function should return false for odd numbers', () => {
expect(isEven(5)).toBe(false);
});
この2つの試験は予想される結果を得ることができるが、これは
isEven
がすべての数字を満たすことを意味しない.この点に基づいて、テストをよりrobustに変更することができます.そうするな
it('isEven function should return true for even numbers', () => {
for(i=0; i < 2000; i+=2){
expect(isEven(i)).toBe(true);
}
});
it('isEven function should return false for odd numbers', () => {
for(i=1; i < 2000; i+=2){
expect(isEven(i)).toBe(false);
}
});
今、私たちがテストしたのは奇数や偶数ではなく、1000個です.これでもっといいですか?なぜ1万個か100万個ではないのですか?もしあなたが通信録に名前を指定する方法があるかどうかを検査するには、10万個の名前をテストするリストを作って、すべてテストする必要がありますか?
すべての状況をカバーすることはできません.この必要もありません.あなたのテストも異常に時間がかかります.1つのテストデータはあなたのコードが正常に動作していることを証明することはできません.実際には、テストのコードが100%正しいことを保証することはできません.
逆に,
例に焦点を当てた.既知の偶数と既知の奇数をテストすることができます.nameをチェックする例では、valid
のいくつかのname、およびinvalid
のいくつかのnameを選択することができる.次の例は
One Assertion Per Test
の原則を破った.同じ方法のために5つの独立したテストを書くつもりはありません.5つの異なる値をテストするために、isEven
を長くすることができます.it('isEven function should return true for even numbers', () => {
expect(isEven(0)).toBe(true);
expect(isEven(4)).toBe(true);
expect(isEven(987239874)).toBe(true);
});
一般的にテストでループを使用しないでください.
Mocks, Stubs, Spies
前述したように、分離ユニット試験の
機能について議論した.それがmocks、stubs、spiesの根源です.Mocks and Stubs
mockとstubは基本的に同じものです:その違いを理解することが重要で、後で議論します.両方とも、testのobjectは、自身が実装した
fake
またはdummy
で置き換えられる.これに先立って,テストは異なるインタフェースから提供されるNavParamsからdataを取得することに依存するtestシナリオについて議論した.コード長はこうです.let myValue = this.navParams.get('someValue');
Page2
をテストしたい場合、NavParamsが提供するdataはPage1
から来ており、他のコンポーネントのデータに依存できないため、セルテストに問題が発生します.実際の
NavParams
を初期に自分で偽造した実装で置き換えることができます.例:class myFakeNavParams {
public get(param: string): any {
return 'hello!';
}
};
これにより、真の
NavParams
のすべての機能が体現されるとともに、get
の方法を置き換え、彼に何が伝わっても、hello!
に戻るだけです.NavParams
の偽造版が完成した後、providersを設定するときにtestに偽造版を使うように伝えるだけです.providers: [
...
{ provide: NavParams, useClass: myFakeNavParams },
...
]
NOTE:このステップはtestの設定時に完了しましたので、後でこれについて議論します.あなたのアプリでこれを本物のproviderに置き換えないでください.
この場合、testで
NavParams
を参照するたびに、作成した偽造版に置き換えられます.前述したように、stubsとmocksは同じものに基づいています.本物のbehaviorを偽造behaviourで置き換えます.しかし、概念的な違いがある.この違いを説明するのは難しいが、私の頭の中の概念が100%正しいとは思わないし、何から構成されたmocksと何から構成されたstubsに100%の共通認識があるとは思わないので、違いが心配だ.
私が最善の解釈を見つけることができるのは、通常stubsがテストのvaluesを返すために使用されることです.テストで使用する戻り値を偽造する場合はstubが必要です.つまり、上記の例は、以下の方法でより適切に使用できます.
spyOn(navParams, 'get').and.returnValue('hello!');
したがって、複雑なクラスを作成する必要はありません.簡単なstubを作成してvalueを返すだけです.しかし、testsは、偽造実装を提供したいobjectの構造に依存する場合がある.loadingのマスクを作成したい場合は、次のmockを作成できます.
export class LoadingControllerMock {
public create(): LoadingComponentMock {
return new LoadingComponentMock();
}
}
これは、テストからテストへの変更ではなく、実際の
LoadingController
の擬似実装です.これにより、loadingを作成するマスクをテストするときに、本物のマスクを使用しないことができます(使用すると隔離テストのルールが破られるため).簡単に言えば、stubsはtestで変更されるvaluesを偽造し、mocksはobjectの構造を偽造し、同じmockの異なるバージョンを作成しません.
Spies
Spiesはmocksとstubsに似ていて、
spy
のためにいくつかのものを出すことができます.一般的な使い方は以下の通りです.spyOn(someObject, 'someMethod');
この場合、そのobjectがその擬似実装に置き換えられるだけでなく、track
someMethod
メソッドの呼び出しも容易に行うことができる.テストのいずれかの点で、Spyはこの方法が呼び出されたかどうか、呼び出されたときに何の情報があったか、何回呼び出されたかなどを教えてくれます.Tests Are Not Fool Proof
テストは万全ではないことを覚えておいてください.合格したテストでは、コードが正常に動作することは保証されません.もしあなたのテストコードがよく書けば、それはあなたのコードがしたいことをしていることを大きく説明しますが、100%保証することはできません.テストが間違っているか、指定したcaseを上書きしていないと失敗することがよくあります.