クラス名を引数としてリダイレクションを行う単純なヘルパーをテストしたいとします。
いくつかのコントローラー内から多くの場所で関数が呼び出された場合、これをどのようにテストする必要がありますか?コード全体でパラメーターとして渡されたすべてのクラス名をテストする必要がありますか(プロバイダー関数で自分で記述します)?それとも私のためにそれを行う魔法の機能はありますか?
あなたの質問は、依存性注入が正しく行われた場合(最も一般的なフレームワークがそれを「実装」する方法ではない)、コードのテスト容易性の究極として宣伝される正確な理由です。
その理由を理解するために、「ヘルパー関数」とクラス指向プログラミングによってコントローラーのテストがどのように困難になるかを見てみましょう。
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
単体テストの全体的なポイントは、コードの「ユニット」が分離して機能することを確認することです。機能を分離できない場合、関連する他のコードの動作によって結果が汚染される可能性があるため、テストは無意味です。これにより、統計家がタイプIおよびタイプIIのエラーと呼ぶものが発生する可能性があります。基本的に、これは、嘘をついている可能性のあるテスト結果を取得できることを意味します。
上記のコードでは、ヘルパーを簡単にモックして、外部の影響から完全に分離しMyController::doSomething
て機能することを判断することはできません。なぜだめですか?ヘルパーメソッドの動作を「モック」して、メソッドが実際にヘルパー結果に100を追加することを保証することはできないためです。ヘルパーの正確な動作(42を返す)に固執しています。これは、正しいオブジェクト指向と制御の反転が完全に排除する問題です。方法の例を考えてみましょう:doSomething
MyController
静的ヘルパー関数を使用する代わりに依存関係を要求する場合、外部の影響をあざけるのは簡単になります。検討:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
さて、テストするのは簡単です。それMyController::doSomething
は実際、留守番電話から返されるものに100を追加します。
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
この例は、コードでインターフェイスを正しく使用することで、テストプロセスを大幅に簡素化できることも示しています。PHPUnitのようなテストフレームワークを使用すると、コードユニットの分離された機能をテストするために、インターフェイス定義をモックして、必要なことを正確に実行できます。
したがって、これらの非常に単純な例が、コードのテストに関して依存性注入がいかに強力であるかを示していることを願っています。しかし、もっと重要なことは、選択したフレームワークが静的(グローバルの単なる別名)、シングルトン、およびヘルパー関数を使用している場合に、なぜ注意する必要があるのかを示してくれることを願っています。
テストする必要のあるすべての関数に対して、可能なパラメーターの組み合わせをテストすることはできません。それは宇宙の生活よりも長くかかります。したがって、ヒューマンインテリジェンスを使用します(一部の人はそれを不正行為と呼ぶかもしれません;-)。一度だけテストします。この場合は、モックコントローラーをパラメーターとして使用します。
次に、コードを見て、渡された他のオブジェクトが実際に異なる動作をするかどうかを自問します。あなたが「単純なヘルパー」と表現するものについては、おそらく答えはノーです。しかし、もしそうなら、どうやって?その異なる動作をシミュレートする別のモックコントローラークラスを作成します。たとえば、この2番目のコントローラーには、ヘルパークラスが呼び出すことを期待する関数がない場合があります。例外がスローされることを期待しています。そのための単体テストを作成します。
満足するまで繰り返します。