2009-07-20 1 views
1

私はPHPUnitを使用していますが、データストアとして使用されているオブジェクトのために良いモックとスタブを作成することは困難です。ユニットデータをPHPでテストする

例:

class urlDisplayer { 
    private $storage; 
    public function __construct(IUrlStorage $storage) { $this->storage = $storage; } 
    public function displayUrl($name) {} 
    public function displayLatestUrls($count) {} 
} 

interface IUrlStorage { 
    public function addUrl($name, $url); 
    public function getUrl($name); 
} 

class MysqlUrlStorage implements IUrlStorage { 
    // saves and retrieves from database 
} 

class NonPersistentStorage implements IUrlStorage { 
    // just stores for this request 
} 

Egがどのように異なる$名前の2回の呼び出しで複数の可能な値を返すPHPUnitのスタブを持っていますか?

編集:サンプルテスト:

public function testUrlDisplayerDisplaysLatestUrls { 
    // get mock storage and have it return latest x urls so I can test whether 
    // UrlDisplayer really shows the latest x 
} 

このテストでモックは、しかし、マニュアルに、私は一つの値を返すことだけか、URLの数を返す必要があります。

+0

最後に私がコメントすることができます。 :) これでテストしようとしていることを明確にできますか? IUrlStorageクラスを単体テストしますか? PHPUnitができないことを達成しようとしていることのより具体的な例を教えてください。ありがとう。 – hobodave

+0

@hobodava:更新済み – koen

答えて

1

あなたの質問はあまり明確ではありませんが、別の状況でphpunitのモックオブジェクトを使用して別の値を返す方法を尋ねていると思いますか?

PHPUnitのモッククラスでは、カスタム関数(つまり、コールバック関数/メソッド)を指定できます。これは実質的に無制限です。

以下の例では、呼び出されるたびにストレージ内の次のURLを返すモックIUrlStorageクラスを作成しました。

public function setUp() 
{ 
    parent::setUp(); 
    $this->fixture = new UrlDisplayer(); //change this to however you create your object 

    //Create a list of expected URLs for testing across all test cases 
    $this->expectedUrls = array(
      'key1' => 'http://www.example.com/url1/' 
     , 'key2' => 'http://www.example.net/url2/' 
     , 'key3' => 'http://www.example.com/url3/' 
    ); 
} 

public function testUrlDisplayerDisplaysLatestUrls { 
    //Init   
    $mockStorage = $this->getMock('IUrlStorage'); 
    $mockStorage->expects($this->any()) 
     ->method('getUrl') 
     ->will($this->returnCallback(array($this, 'mockgetUrl'))); 

    reset($this->expectedUrls); //reset array before testing 

    //Actual Tests 
    $this->assertGreaterThan(0, count($this->expectedUrls)); 
    foreach ($this->expectedUrls as $key => $expected) { 
     $actual = $this->fixture->displayUrl($key); 
     $this->assertEquals($expected, $actual); 
    } 
} 

public function mockGetUrl($name) 
{ 
    $value = current($this->expectedUrls); 
    next($this->expectedUrls); 

    //Return null instead of false when end of array is reached 
    return ($value === false) ? null : $value; 
} 

また、時には単に必要な機能を皮肉って、実際のクラスを作成する方が簡単です。これは、明確に定義された小さなインタフェースでは特に簡単です。この特定のケースで

、私が代わりに以下のクラスを使用することをお勧めします:

class MockStorage implements IUrlStorage 
{ 
    protected $urls = array(); 

    public function addUrl($name, $url) 
    { 
     $this->urls[$name] = $url; 
    } 

    public function getUrl($name) 
    { 
     if (isset($this->urls[$name])) { 
      return $this->urls[$name]; 
     } 
     return null; 
    } 
} 
?> 

次に、あなたのユニットテストクラスにあなたは、単に以下のようなあなたのフィクスチャをインスタンス化:

public function setUp() { 
    $mockStorage = new MockStorage(); 

    //Add as many expected URLs you want to test for 
    $mockStorage->addUrl('name1', 'http://example.com'); 
    //etc... 

    $this->fixture = new UrlDisplayer($mockStorage); 
} 
関連する問題