2011-05-11 115 views
10

考慮如何斷言模擬對象的函數調用的對象參數

class Foo { 
    public $att; 
    public function __construct ($a) { $this->att = $a; } 
} 

class Some { 
    public function callMe (Foo $f) {} 
} 

// class I want to test 
class SuT { 
    public function testMe (Some $s) { 
     echo $s->callMe(new Foo('hi')); 
    } 
} 

我要檢查是否Sut::testMe()正確調用Some::callMe()。由於該參數是一個(Foo)對象(不是標量類型),我無法弄清楚如何調用PHPUnit的with()來在其上運行斷言。例如,有一個assertAttributeEquals方法,但是如何爲它提供調用的參數?

我想要做的是這樣的:

class SuTTest extends PHPUnit_Framework_TestCase { 
    public function testSuT() { 
     $stub = $this->getMock('Some'); 
     $stub->expects($this->once())->method('callMe') 
      ->with($this->assertAttributeEquals('hi', 'att', $this->argument(0)); 

     /* 
     * Will $stub->callMe be called with a Foo object whose $att is 'hi'? 
     */ 
     $sut = new SuT(); 
     $sut->testMe($stub); 
    } 
} 

回答

8

你只是通過預期值到「用」的方法。

->with(1, $object, "paramThree"); 

還可以通過在一系列的PHPUnit斷言,而不是參數(默認爲等於)

->with(1, $this->equalTo($object), "paramThree"); 

所以對於對象你會使用$this->isInstanceOf("stdClass")作爲參數來->with

查看可能的斷言列表:PHPUnit/Framework/Assert.php

對於返回new PHPUnit_Framework_Constraint


小的演示

第一個測試用例功能非常符合2個參數和工作

第二個匹配的是兩個和失敗的說法2

最後一個測試傳入的對象是類型stdClass

<?php 

class MockMe { 
    public function bla() { 

    } 
} 

class Demo { 

    public function foo(MockMe $x) { 
     $x->bla(1, 2); 
    } 

    public function bar(MockMe $x) { 
     $x->bla(1, new stdClass()); 
    } 

} 

class DemoTest extends PHPUnit_Framework_TestCase { 

    public function testWorks() { 
     $x = new Demo(); 
     $mock = $this->getMock("MockMe"); 
     $mock->expects($this->once())->method("bla")->with(1,2); 
     $x->foo($mock); 
    } 

    public function testFails() { 
     $x = new Demo(); 
     $mock = $this->getMock("MockMe"); 
     $mock->expects($this->once())->method("bla")->with(1,3); 
     $x->foo($mock); 
    } 

    public function testObject() { 
     $x = new Demo(); 
     $mock = $this->getMock("MockMe"); 
     $mock->expects($this->once())->method("bla")->with(1, $this->isInstanceOf("stdClass")); 
     $x->bar($mock); 
    } 
} 

結果:

phpunit DemoTest.php 
PHPUnit 3.5.13 by Sebastian Bergmann. 

.F. 

Time: 0 seconds, Memory: 4.25Mb 

There was 1 failure: 

1) DemoTest::testFails 
Failed asserting that <integer:2> matches expected <integer:3>. 

...DemoTest.php:12 
...DemoTest.php:34 

FAILURES! 
Tests: 3, Assertions: 2, Failures: 1. 
+0

感謝您的詳細解答!這兩個提議的斷言('instanceOf'和'equals($ object)')都可以正常工作,但我想要更多細粒度的斷言(例如檢查對象的屬性或斷言對象的函數返回值之一)! – JayVee 2011-05-13 07:14:29

+0

@JayVee:檢查函數不是以PHPUnit/Framework/Assert.php(上面的鏈接)中的「assert」開頭。你可能要找的是:' - > attributeEqualTo($ attributeName,$ value)'對象的屬性,如果你真的想檢查傳入的對象在調用函數時返回正確的值(不確定如果你真的需要這樣的話,也許有更簡單的方法來構建你的測試?)你需要(afaik)創建你自己的PHPUnit_Framework_Constraint_ *類。 – edorian 2011-05-13 07:31:50

+0

啊,這就是訣竅! '''attributeEqualTo''完全按照我預期(和需要)的方式工作。再次感謝! – JayVee 2011-05-23 09:28:29

7

這是一個簡單的例子,做你所需要的:

$mock->expects ($this->once()) 
    ->method ('dummyFunction') 
    ->with ($this->logicalAnd ($this->isInstanceOf ('DummyClass') 
           ,$this->attributeEqualTo ('attribute1', 1001) 
           ,$this->attributeEqualTo ('attribute2', 200))) 
    ->will ($this->returnValue (null)); 

和代碼的其餘部分:

class DummyClass { 
    private $attribute1 = 1001; 
    private $attribute2 = 200; 
} 
function dummyFunction (DummyClass $p) {...} 
dummyFunction (new DummyClass()); 

我希望我已幫助你

+0

太棒了,簡直不敢相信它! – JayVee 2011-05-23 09:25:39

10

儘管問題已經得到了充分的回答(如何聲明作爲模擬參數傳遞的對象的屬性),我認爲值得注意的是PHPUnit支持使用()傳遞的回調約束。

我一直試圖找出如何在模擬對象參數上運行更多斷言的同時,一直與此線程碰撞。例如,我需要檢查某種方法的返回值。顯然,爲了理智,沒有methodReturnValueEqualTo()等價於上面答案中使用的屬性聲明。

幸運的是,PHPUnit確實支持(至少3.7)回調約束,這很有意義,並且可以在專門的模擬庫(如Mockery)中找到。下面

PHPUnit version docs當前狀態:

回調()約束可以用於更復雜的參數驗證。該約束以PHP回調爲唯一參數。 PHP回調將接收參數作爲唯一參數進行驗證,如果參數通過驗證,則返回TRUE,否則返回FALSE。

因此,使用回調約束,在OP例如現在可以表示爲:

class SuTTest extends PHPUnit_Framework_TestCase { 
    public function testSuT() { 
     $stub = $this->getMock('Some'); 
     $stub->expects($this->once()) 
      ->method('callMe') 
      ->with($this->callback(function($arg) { 
        return ($arg instanceof Some) && ($arg->att === 'hi'); 
       }) 
      ); 

     /* 
     * Will $stub->callMe be called with a Foo object whose $att is 'hi'? 
     */ 
     $sut = new SuT(); 
     $sut->testMe($stub); 
    } 
} 

和測試失敗會看起來像:

1)SuTTest :: testSuT
方法名稱的期望失敗等於<字符串:callMe >被調用時1次
參數0 for i nvocation Some :: callMe(Foo Object(...))與預期值不匹配。
無法斷言Foo Object()被指定的回調接受。

您現在可以對該參數運行任何邏輯。

更妙的是,儘管不是在PHPUnit的文檔文件化功能,可以實際使用的斷言,並得到斷言錯誤消息的好處:

class SuTTest extends PHPUnit_Framework_TestCase { 
    public function testSuT() { 
     // alias to circumvent php closure lexical variable restriction 
     $test = $this; 
     // proceed as normal 
     $stub = $this->getMock('Some'); 
     $stub->expects($this->once()) 
      ->method('callMe') 
      // inject the test case in the closure 
      ->with($this->callback(function($arg) use ($test) { 
        // use test assertions 
        $test->assertInstanceOf('Some', $arg); 
        $test->assertAttributeEquals('hi', 'att', $arg); 
        // return true to satisfy constraint if all assertions passed 
        return true; 
       }) 
      ); 

     /* 
     * Will $stub->callMe be called with a Foo object whose $att is 'hi'? 
     */ 
     $sut = new SuT(); 
     $sut->testMe($stub); 
    } 
} 

真的不知道如何證明未來是這一戰略使用斷言並返回true。它沒有記錄,並且在錯誤消息中存在權衡。你不再得到參數約束信息,所以如果你在多個參數上設置斷言,你將不得不推斷,如果可能的話,哪一個失敗。但是你可以更好地描述關閉中失敗的斷言。

1)SuTTest :: testSuT
期望失敗方法名是等於<字符串:的CallMe >調用1周時間(s)時
失敗斷言兩個字符串相等。
---預計
+++實際
@@ @@
-'hi」
+ '不'

希望這有助於任何人都面臨着類似的問題。

+0

謝謝,你是個大人物!,你幫了我太多 – jjoselon 2017-06-02 20:10:40