PHPUnit withConsecutive 奇怪的行为

PHPUnit withConsecutive strange behaviour

考虑下一个测试:

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(...) 为对象写很多约束?

并非 PHPUnit 中的所有内容都是完美的,但这应该适合您。当然属性'name'只是一个例子。如果用户不持有任何行为,你不需要模拟它。

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);
}

试试这个代码:

 $userRepository
     ->expects($this->exactly(2))
     ->method('getByUser')
     ->withConsecutive(
         [$this->identicalTo($user1)],
         [$this->identicalTo($user2)]
     );