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)]
);
考虑下一个测试:
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)]
);