2016-08-18 67 views
0

考慮下一個測試:PHPUnit的withConsecutive奇怪的行爲

class User 
{ 
} 

class UserRepository 
{ 
    public function getByName($name) 
    { 
    } 

    public function getByUser(User $user) 
    { 
    } 
} 

class UserController 
{ 
    private $userRepository; 

    public function __construct(UserRepository $userRepository) 
    { 
     $this->userRepository = $userRepository; 
    } 

    public function findByNames($name1, $name2) 
    { 
     $this->userRepository->getByName($name1); 
     $this->userRepository->getByName($name2); 
    } 

    public function findByUsers($user1, $user2) 
    { 
     $this->userRepository->getByUser($user1); 
     $this->userRepository->getByUser($user2); 
    } 
} 

class WithConsecutiveTest extends \PHPUnit_Framework_TestCase 
{ 
    /** 
    * This test is fails if some of "Alice" or "Bob" string changed. This is expected behaviour. 
    */ 
    public function testWithConsecutiveOnStrings() 
    { 
     $name1 = 'Alice'; 
     $name2 = 'Bob'; 

     $userRepository = $this->createMock(UserRepository::class); 

     $userRepository 
      ->expects($this->exactly(2)) 
      ->method('getByName') 
      ->withConsecutive(
       [$name1], // change to $name2 and test fails 
       [$name2] 
      ); 

     $userController = new UserController($userRepository); 
     $userController->findByNames($name1, $name2); 
    } 

    /** 
    * This test is NOT fails if in "withConsecutive()" call $user1 changed to $user2. This is unexpected behaviour. 
    */ 
    public function testWithConsecutiveOnObjects() 
    { 
     $user1 = $this->createMock(User::class); 
     $user2 = $this->createMock(User::class); 

     $this->assertEquals($user1, $user2); 
     $this->assertNotSame($user1, $user2); 

     $userRepository = $this->createMock(UserRepository::class); 

     $userRepository 
      ->expects($this->exactly(2)) 
      ->method('getByUser') 
      ->withConsecutive(
       [$user1], // change to $user2 and test is also passed 
       [$user2] 
      ); 

     $userController = new UserController($userRepository); 
     $userController->findByUsers($user1, $user2); 
    } 
} 

與字符串參數的第一次測試「withConsecutive()」是正常工作,但使用對象第二個測試做了一些魔法:類似的兩個弱對比的東西對象在這裏,所以無論如何第二個測試通過。

我試過用「[$ this-> callback(function($ user1arg)use($ user1)){return $ user1arg === $ user1}]」約束 - 這很好,但寫了很多這個約束是某種猴子的工作。

也許它可能有一個更簡單的解決方案,而不是用$ this-> callback(...)爲對象編寫很多約束條件?

回答

1

並非PHPUnit中的所有內容都是完美的,但這應該適用於您。當然,「名稱」只是一個例子。如果用戶不持有任何行爲,則不需要嘲笑它。

class User 
{ 
    private $name; 

    public function setName($name) 
    { 
     $this->name = $name; 
    } 

    public function getName($name) 
    { 
     return $this->name; 
    } 
} 

測試方法

public function testWithConsecutiveOnObjects() 
{ 
    $user1 = new User(); 
    $user1->setName('Alice'); 
    $user2 = new User(); 
    $user2->setName('Bob'); 
    $this->assertNotEquals($user1, $user2); 
    $this->assertNotSame($user1, $user2); 
    $userRepository = $this->createMock(UserRepository::class); 
    $userRepository 
     ->expects($this->exactly(2)) 
     ->method('getByUser') 
     ->withConsecutive(
      [$user1], //we are comparing values here, not references 
      [$user2] 
     ); 
    $userController = new UserController($userRepository); 
    $userController->findByUsers($user1, $user2); 
} 
+0

看起來像一個醜陋的黑客攻擊:)工作,但是很奇怪的解決方案:讓測試代碼的理解對其他開發人員更加複雜。 – Alex

+0

正如我在評論中解釋的那樣,你需要讓對象具有不同的值。這個斷言是比較值,而不是引用。 –

+0

哦,那麼,PHPUnit沒有斷言,比較引用? – Alex