テスト対象に依存します。しかし、テストの範囲はあなた自身のコードのみであると仮定しましょう。その場合、以下をテストする必要があります。
- 「ハッピーケース」:アプリケーションに有効な入力を送り、正しい出力が生成されることを確認します。
- 失敗のケース:アプリケーションに無効な入力をフィードし、それらが正しく処理されることを確認します。
これを行うには、同僚のコンポーネントを使用できません。代わりに、mockingを使用します。つまり、アプリケーションの残りの部分を、テストフレームワークから制御できる「偽の」モジュールに置き換えます。これを正確に行う方法は、モジュールのインターフェイス方法によって異なります。ハードコードされた引数を使用してモジュールのメソッドを呼び出すだけで十分であり、他のモジュールのパブリックインターフェイスをテスト環境に接続するフレームワーク全体を記述するのと同じくらい複雑になります。
ただし、これはユニットテストケースにすぎません。統合テストも必要です。統合テストでは、すべてのモジュールをまとめてテストします。繰り返しますが、幸せなケースと失敗の両方をテストする必要があります。
「基本的な例」の場合、コードを単体テストするには、データベース層をシミュレートする模擬クラスを作成します。ただし、モッククラスは実際にはデータベースに移動しません。予想される入力と固定出力をプリロードするだけです。擬似コードで:
function test_ValidUser() {
// set up mocking and fixtures
userid = 23;
db = new MockDB();
db.fixedResult = { firstName: "John", lastName: "Doe" };
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
expectedResult = "John Doe";
// run the actual test
actualResult = userController.displayUserAsString(userid);
// check assertions
assertEquals(expectedResult, actualResult);
db.assertExpectedCall();
}
そして、正しく報告される欠落フィールドをテストする方法は次のとおりです。
function test_IncompleteUser() {
// set up mocking and fixtures
userid = 57;
db = new MockDB();
db.fixedResult = { firstName: "John", lastName: "NA" };
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
// let's say the user controller is specified to leave "NA" fields
// blank
expectedResult = "John";
// run the actual test
actualResult = userController.displayUserAsString(userid);
// check assertions
assertEquals(expectedResult, actualResult);
db.assertExpectedCall();
}
物事が面白くなりました。実際のDBクラスが正しく動作しない場合はどうなりますか?たとえば、不明な理由で例外をスローする可能性があります。実行されるかどうかはわかりませんが、独自のコードで適切に処理する必要があります。問題ありません。MockDBに例外をスローさせるだけです。たとえば、次のようなメソッドを追加します。
class MockDB {
// ... snip
function getUser(userid) {
if (this.fixedException) {
throw this.fixedException;
}
else {
return this.fixedResult;
}
}
}
そして、テストケースは次のようになります。
function test_MisbehavingUser() {
// set up mocking and fixtures
userid = 57;
db = new MockDB();
db.fixedException = new SQLException("You have an error in your SQL syntax");
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
// run the actual test
try {
userController.displayUserAsString(userid);
}
catch (DatabaseException ex) {
// This is good: our userController has caught the raw exception
// from the database layer and wrapped it in a DatabaseException.
return TEST_PASSED;
}
catch (Exception ex) {
// This is not good: we have an exception, but it's the wrong kind.
testLog.log("Found the wrong exception: " + ex);
return TEST_FAILED;
}
// This is bad, too: either our mocking class didn't throw even when it
// should have, or our userController swallowed the exception and
// discarded it
testLog.log("Expected an exception to be thrown, but nothing happened.");
return TEST_FAILED;
}
これらはユニットテストです。統合テストでは、MockDBクラスを使用しません。代わりに、両方の実際のクラスを連結します。まだフィクスチャが必要です。たとえば、テストを実行する前に、テストデータベースを既知の状態に初期化する必要があります。
さて、責任に関する限り、コードはコードベースの残りが仕様ごとに実装されることを期待する必要がありますが、残りが台無しになったときに適切に処理できるように準備する必要があります。自分以外のコードをテストするための責任を負いませんが、あなたはしているもう一方の端でコードを不正な動作へのあなたのコードは、弾性作るための責任、そしてまた、あなたのコードの回復力をテストするための責任があります。これが、上記の3番目のテストで行われます。