PHPUnit で依存関係のあるオブジェクトをテストする
-
23-08-2019 - |
質問
実装の一部として別のオブジェクトを構成するオブジェクトの場合、主要なオブジェクトのみがテストされるように単体テストを記述する最良の方法は何でしょうか?些細な例:
class myObj {
public function doSomethingWhichIsLogged()
{
// ...
$logger = new logger('/tmp/log.txt');
$logger->info('some message');
// ...
}
}
ロガー オブジェクトの依存関係を挿入して単体テストでモックできるようにオブジェクトを設計できることはわかっていますが、常にそうとは限りません。より複雑なシナリオでは、他のオブジェクトを構成するか、静的メソッドを呼び出す必要があります。 。
ロガー オブジェクトではなく、myObj のみをテストしたいのですが、どうすればよいでしょうか?テストスクリプトでスタブ化された「ダブル」を作成しますか?何かのようなもの:
class logger
{
public function __construct($filepath) {}
public function info($message) {}
}
class TestMyObj extends PHPUnit_Framework_TestCase
{
// ...
}
これは小さなオブジェクトでは実現可能のように思えますが、SUT が戻り値に依存するような複雑な API では困難になるでしょう。また、モック オブジェクトの場合と同じように依存関係オブジェクトへの呼び出しをテストしたい場合はどうすればよいでしょうか?渡されるのではなく、SUTによってインスタンス化されるオブジェクトをモックする方法はありますか?
モックに関するマニュアルページを読みましたが、依存関係が集約されるのではなく構成されるこの状況についてはカバーされていないようです。どうやってやるのですか?
解決
あなたがすでに認識しているように見えるとして、具体的なクラスの依存関係は、テストハード(または完全に不可能)になります。あなたは、その依存関係を分離する必要があります。既存のAPIを壊さない簡単な変更は、現在の動作にデフォルト設定が、それを無効にするフックを提供することです。これは実装することができ、いくつかの方法があります。
一部の言語ではコードにモッククラスを注入することができるツールを持っているが、私はPHPのために、このような何も知りません。ほとんどの場合、あなたはおそらくとにかくあなたのコードをリファクタリングしたほうが良いでしょう。
他のヒント
続く トロエルスキン ここに、あなたがすべきことの基本的な例を示します。
<?php
class MyObj
{
/**
* @var LoggerInterface
*/
protected $_logger;
public function doSomethingWhichIsLogged()
{
// ...
$this->getLogger()->info('some message');
// ...
}
public function setLogger(LoggerInterface $logger)
{
$this->_logger = $logger;
}
public function getLogger()
{
return $this->_logger;
}
}
class MyObjText extends PHPUnit_Framework_TestCase
{
/**
* @var MyObj
*/
protected $_myObj;
public function setUp()
{
$this->_myObj = new MyObj;
}
public function testDoSomethingWhichIsLogged()
{
$mockedMethods = array('info');
$mock = $this->getMock('LoggerInterface', $mockedMethods);
$mock->expects($this->any())
->method('info')
->will($this->returnValue(null));
$this->_myObj->setLogger($mock);
// do your testing
}
}
モックオブジェクトの詳細については、こちらをご覧ください。 マニュアルの中で.
質問を誤解していたようです、もう一度質問させてください:
まだ手遅れでない場合は、ロガーのシングルトン パターンまたはファクトリを使用する必要があります。
class LoggerStub extends Logger {
public function info() {}
}
Logger::setInstance(new LoggerStub());
...
$logger = Logger::getInstance();
コードを変更できない場合は、オーバーロードしているキャッチオール クラスを使用できます。 __電話()
class GenericStub {
public function __call($functionName, $arguments) {}
}
は、実際にはPHPUnitを構築し、同じ男が発表PHPクラスのオーバーロードのための合理的に新しい拡張機能があります。それはあなたが、残念ながらそれをWindowsにインストールすることは簡単ではない、あなたがコードをリファクタリングすることができない場合にnew演算子をオーバーライドすることができます。