セッション管理クラスと関連する単体テストを構築しています。クラスを$_SESSIONのグローバル状態から分離するために、非常に単純なクラスを使用して、クラスとセッションデータ間のバインディングを管理しています。
Bindingクラスのソース全体は次のとおりです。
class Binding implements iface\Binding
{
public function &getNamespace ($namespace)
{
return $_SESSION [$namespace];
}
}
消費するSessionクラスには、次のものがあります。
protected function initStorage ()
{
// Check that storage hasn't already been bound to the session
if ($this -> storage === NULL)
{
// Attempt to start the session if it hasn't already been started
if (($this -> sessionId () !== '')
|| ((!$this -> headersSent ())
&& ($this -> startSession ())))
{
// Bind the storage to the session
$this -> storage =& $this -> binding -> getNamespace ($this -> namespace);
// Make sure the session is in a usable state
if (!$this -> hasData ())
{
$this -> reset ();
}
}
else
{
// We couldn't start the session
throw new \RuntimeException (__METHOD__ . ': Unable to initiate session storage at this time');
}
}
return $this;
}
sessionId、headersSent、startSessionは、「テストシーム」として機能する単純な1行の関数であり、PHPUnitのモックに簡単に置き換えることができます。
テストを書いているときに、セッションをクラスから切り離すだけでなく、モックバインディングクラスを使用してより多くのことができることに気付きました。また、実際に内部を公開しなくても、クラスの非公開プロパティを監視する方法として使用できることもわかりました。状態、したがってクラスを脆弱にします。クラスは配列を直接操作するのではなく、配列への参照を操作するため、参照されている配列を観察できます。
PHPUnitのモックAPIでこれを実行したいと思っていましたが、その方法がわかりません。
次のような配列を返すモックを作成できることはわかっています。
$mock = $this -> getMock ('iface\Binding');
$mock -> expects ($this -> any ())
-> method ('getNamespace')
-> will ($this -> returnValue (array ()));
ただし、毎回異なる配列を返すため、状態の変化を観察するのには役立ちません。必要なのは、毎回同じ配列への参照を返すモックです。
最後に、実際のBindingクラスの代わりにクラスを作成し、代わりにそれを使用します。
class BindingMock implements iface\Binding
{
protected $storage = array ();
public function &getNamespace ($namespace)
{
return $this -> storage [$namespace];
}
}
このクラスを使用すると、Sessionクラスで非公開状態を公開しなくても、ストレージ配列の内容を確認できるため、SessionAPIで何かを呼び出す前後の$storageの内容を調べることができます。この手法を使用したテストの例を次に示します。
public function testCreateItem ()
{
$storage =& $this -> binding -> getNamespace ('unittest');
$this -> assertEmpty ($storage);
$this -> object -> createItem ('This is a test', 'test');
$this -> assertNotEmpty ($storage);
$this -> assertEquals ('This is a test', $storage ['test']);
}
PHPUnitを使用して代替クラスを生成できるようにしたいと思いますが、PHPUnitで同じことを実現する方法がない限り、単体テスト専用の追加クラスを作成するのは間違った方法のようです。