:私のようなメソッドを呼び出す必要はありません。
私は完全なDI(例えば、Zend Framework MVC)を行うことができませんが、サービスロケータを使用します。サービスレイヤは、すべてのクラスがそこから依存関係を取得するための場所になります。あなたのクラスの依存関係のための1つの深い抽象化と考えることができます。 Service Locatorの使用には多くの利点がありますが、ここではテスト容易性に焦点を当てます。
のは、いくつかのコードに取得しましょう、ここにあるそれがないすべてのメソッド「検索」が呼ばれない限り、それ自体を返しているモデルのクエリクラス
class SomeModelQuery
{
public function __call($method, $params)
{
if ($method == 'find') {
return 'Real Data';
}
return $this;
}
}
です。次に、ハードコーディングされた文字列 "Real Data"を返します。
今私達のサービスロケータ:
class ServiceLocator
{
protected static $instance;
protected $someModelQuery;
public static function resetInstance()
{
static::$instance = null;
}
public static function instance()
{
if (self::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getSomeModelQuery()
{
if ($this->someModelQuery === null) {
$this->someModelQuery = new SomeModelQuery();
}
return $this->someModelQuery;
}
public function setSomeModelQuery($someModelQuery)
{
$this->someModelQuery = $someModelQuery;
}
}
これには2つのことを行います。グローバルスコープメソッドインスタンスを提供し、いつでも取得できます。それをリセットさせることと一緒に。次に、モデルクエリオブジェクトのgetおよびsetメソッドを提供します。まだ設定されていない場合は遅延ロードを使用します。
今すぐ実際の作業を行うコード:
class Foo
{
public function doSomething()
{
return ServiceLocator::instance()
->getSomeModelQuery()->filterByFoo('bar')->find();
}
}
fooがサービスロケータを呼び出して、それはそれからクエリオブジェクトのインスタンスを取得し、それがそのクエリオブジェクト上に必要呼び出しを行います。
ここで、このすべてのための単体テストを書く必要があります。ここには:
class FooTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
ServiceLocator::resetInstance();
}
public function testNoMocking()
{
$foo = new Foo();
$this->assertEquals('Real Data', $foo->doSomething());
}
public function testWithMock()
{
// Create our mock with a random value
$rand = mt_rand();
$mock = $this->getMock('SomeModelQuery');
$mock->expects($this->any())
->method('__call')
->will($this->onConsecutiveCalls($mock, $rand));
// Place the mock in the service locator
ServiceLocator::instance()->setSomeModelQuery($mock);
// Do we get our random value back?
$foo = new Foo();
$this->assertEquals($rand, $foo->doSomething());
}
}
ここでは、実際のクエリコードが呼び出され、クエリコードが偽装された例を挙げました。
これにより、単体テストするクラスにすべての依存関係を注入する必要がないモックを注入することができます。
上記のコードを書くには多くの方法があります。概念の証明としてそれを使用し、あなたのニーズにそれを適応させる。
他にも素晴らしいですが、私はシングルトンのファンではありません。なしでこれを行う方法はありますか?また、実際にテストデータを作成し、テストごとに削除してテストデータで再作成するオプションもありますが、それは少し遅いです。 – Tower
私が言ったように、これを行う方法はたくさんあります。あなたがシングルトンを避けようとしているなら、必要な場所にService Locatorを挿入することができます。また、テスト容易化のためだけにオプション注入を行うこともできます。つまり、依存関係が設定されている場合はそれを使用し、それ以外の場合は独自のオブジェクトを作成するか、オブジェクトのグローバルインスタンスを取得します。これにより、生産コードで簡単に使用でき、テストコードにモックを挿入することができます。消費するクラスが独自のインスタンスを作成するようにサービスロケータを設定できます。しかし、真のDIのないものは、DB接続のようなシングルトンである必要があります – SamHennessy