变量在 PHPUnit 测试中意外更改
Variable unexpectedly being changed in PHPUnit test
我写了一个简单的单元测试,它正在测试 class 上的一个方法被测试 returns 是它自己的一个实例,它传递的参数未被改变。
即$this->theClass->theMethod($param1, $param2);
不应修改 $param1
或 $param2
作为方法 returns $this
。但是,我看到当我在 运行 方法之后立即 var_dump($param1)
时,$param1 已被修改。
要提供更多背景信息:
假设有一个名为 Bob Jones 的 User
。
$user = factory(App\User::class, 'withCompany')
->make([
'name' => 'Bob',
'surname' => 'Jones',
'member_id' => 1
]);
和 Bob 有一些元 UserData
,它也有属性 name
和 surname
(等值) .
$userData = factory(App\UserData::class)
->make($this->mapUserToUserData($user) + ['member_id' => 1]);
当我将 UserData
元数据对象中的 name
属性更改为“Fred”时,
$userData->m_field_id_4 = 'Fred';
并且我在对象之间同步数据(即我调用被测方法)
$this->syncUserData->sync($user, $userData);
注意:此时 - 如果我```var_dump($user->name);```,输出的是'*Fred*',而不是'*Bob*',因为我希望。
那么 User
对象的名称仍应为“Bob”。
$originalUser = $this->syncUserData->getOriginalUser();
$syncedUser = $this->syncUserData->getSyncedUser();
$this->assertEquals('Bob', $originalUser->name);
$this->assertEquals('Fred', $syncedUser->name);
如上面的注释所述,$user
对象已更改,没有任何直接修改。
为了完整起见,完整代码如下:
SyncUserDataTest.php
class SyncUserDataTest extends TestCase
{
use UserDataMapper;
protected $syncUserData;
public function setUp()
{
parent::setUp();
$this->syncUserData = new App\Helpers\SyncUserData();
}
public function test_it_returns_the_unmodified_user()
{
$user = factory(App\User::class, 'withCompany')
->make([
'name' => 'Bob',
'member_id' => 1
]);
$userData = factory(App\UserData::class)
// And the data is the same
->make($this->mapUserToUserData($user) + ['member_id' => 1]);
$userData->m_field_id_4 = 'Fred';
$this->syncUserData->sync($user, $userData);
$originalUser = $this->syncUserData->getOriginalUser();
$syncedUser = $this->syncUserData->getSyncedUser();
$this->assertEquals('Bob', $originalUser->name);
$this->assertEquals('Fred', $syncedUser->name);
}
}
SyncUserData.php
<?php
namespace App\Helpers;
class SyncUserData
{
/**
* Has any data changed.
*
* @var bool
*/
private $hasChanged = false;
/**
* The original User object without modifications.
*
* @var \App\User
*/
private $originalUser;
/**
* The updated User object that has had data synced from the UserData object.
*
* @var \App\User
*/
private $syncedUser;
/**
* Sync data from UserData with a User object.
*
* @param \App\User $user
* @param \App\UserData $against
* @return $this
*/
public function sync($user, $against)
{
$this->setOriginalUser($user);
$updatedUser = $this->applyChangesToUser($user, $against);
$this->setSyncedUser($updatedUser);
return $this;
}
/**
* Check if two values are the same.
*
* @param string $check
* @param string $against
* @return bool
*/
public function hasChanged($check, $against)
{
if ($check != $against) {
$this->hasChanged = true;
return true;
}
return false;
}
/**
* @param \App\User $user
* @return $this
*/
public function setSyncedUser($user)
{
$this->syncedUser = $user;
return $this;
}
/**
* @param \App\User $user
* @return $this
*/
public function setOriginalUser($user)
{
$this->originalUser = $user;
return $this;
}
/**
* Get the updated User object with data synced from the UserData object.
*
* @access public
* @return mixed
*/
public function getSyncedUser()
{
return $this->syncedUser;
}
/**
* Get the original User object.
*
* @access public
* @return mixed
*/
public function getOriginalUser()
{
return $this->originalUser;
}
/**
* Was any data different between the User and UserData objects.
*
* @access public
* @return bool
*/
public function wasDataChanged()
{
return $this->hasChanged;
}
/**
* @param $user
* @param $against
*/
private function applyChangesToUser($user, $against)
{
if ($this->hasChanged($user->name, $against->m_field_id_4)) {
$user->name = $against->m_field_id_4;
}
if ($this->hasChanged($user->surname, $against->m_field_id_5)) {
$user->surname = $against->m_field_id_5;
}
return $user;
}
}
很简单,我的问题是 - $user
对象的 name
属性是如何改变的?
您似乎遇到的问题是默认情况下对象是通过引用传递的,因此当您更新一个对象时,您会更新所有对象。
解决此问题的方法包括(但可能不限于):
- 使用克隆(虽然这只是一个浅拷贝,所以要小心)
- 将对象序列化然后反序列化为新变量
我写了一个简单的单元测试,它正在测试 class 上的一个方法被测试 returns 是它自己的一个实例,它传递的参数未被改变。
即$this->theClass->theMethod($param1, $param2);
不应修改 $param1
或 $param2
作为方法 returns $this
。但是,我看到当我在 运行 方法之后立即 var_dump($param1)
时,$param1 已被修改。
要提供更多背景信息:
假设有一个名为 Bob Jones 的 User
。
$user = factory(App\User::class, 'withCompany')
->make([
'name' => 'Bob',
'surname' => 'Jones',
'member_id' => 1
]);
和 Bob 有一些元 UserData
,它也有属性 name
和 surname
(等值) .
$userData = factory(App\UserData::class)
->make($this->mapUserToUserData($user) + ['member_id' => 1]);
当我将 UserData
元数据对象中的 name
属性更改为“Fred”时,
$userData->m_field_id_4 = 'Fred';
并且我在对象之间同步数据(即我调用被测方法)
$this->syncUserData->sync($user, $userData);
注意:此时 - 如果我```var_dump($user->name);```,输出的是'*Fred*',而不是'*Bob*',因为我希望。
那么 User
对象的名称仍应为“Bob”。
$originalUser = $this->syncUserData->getOriginalUser();
$syncedUser = $this->syncUserData->getSyncedUser();
$this->assertEquals('Bob', $originalUser->name);
$this->assertEquals('Fred', $syncedUser->name);
如上面的注释所述,$user
对象已更改,没有任何直接修改。
为了完整起见,完整代码如下:
SyncUserDataTest.php
class SyncUserDataTest extends TestCase
{
use UserDataMapper;
protected $syncUserData;
public function setUp()
{
parent::setUp();
$this->syncUserData = new App\Helpers\SyncUserData();
}
public function test_it_returns_the_unmodified_user()
{
$user = factory(App\User::class, 'withCompany')
->make([
'name' => 'Bob',
'member_id' => 1
]);
$userData = factory(App\UserData::class)
// And the data is the same
->make($this->mapUserToUserData($user) + ['member_id' => 1]);
$userData->m_field_id_4 = 'Fred';
$this->syncUserData->sync($user, $userData);
$originalUser = $this->syncUserData->getOriginalUser();
$syncedUser = $this->syncUserData->getSyncedUser();
$this->assertEquals('Bob', $originalUser->name);
$this->assertEquals('Fred', $syncedUser->name);
}
}
SyncUserData.php
<?php
namespace App\Helpers;
class SyncUserData
{
/**
* Has any data changed.
*
* @var bool
*/
private $hasChanged = false;
/**
* The original User object without modifications.
*
* @var \App\User
*/
private $originalUser;
/**
* The updated User object that has had data synced from the UserData object.
*
* @var \App\User
*/
private $syncedUser;
/**
* Sync data from UserData with a User object.
*
* @param \App\User $user
* @param \App\UserData $against
* @return $this
*/
public function sync($user, $against)
{
$this->setOriginalUser($user);
$updatedUser = $this->applyChangesToUser($user, $against);
$this->setSyncedUser($updatedUser);
return $this;
}
/**
* Check if two values are the same.
*
* @param string $check
* @param string $against
* @return bool
*/
public function hasChanged($check, $against)
{
if ($check != $against) {
$this->hasChanged = true;
return true;
}
return false;
}
/**
* @param \App\User $user
* @return $this
*/
public function setSyncedUser($user)
{
$this->syncedUser = $user;
return $this;
}
/**
* @param \App\User $user
* @return $this
*/
public function setOriginalUser($user)
{
$this->originalUser = $user;
return $this;
}
/**
* Get the updated User object with data synced from the UserData object.
*
* @access public
* @return mixed
*/
public function getSyncedUser()
{
return $this->syncedUser;
}
/**
* Get the original User object.
*
* @access public
* @return mixed
*/
public function getOriginalUser()
{
return $this->originalUser;
}
/**
* Was any data different between the User and UserData objects.
*
* @access public
* @return bool
*/
public function wasDataChanged()
{
return $this->hasChanged;
}
/**
* @param $user
* @param $against
*/
private function applyChangesToUser($user, $against)
{
if ($this->hasChanged($user->name, $against->m_field_id_4)) {
$user->name = $against->m_field_id_4;
}
if ($this->hasChanged($user->surname, $against->m_field_id_5)) {
$user->surname = $against->m_field_id_5;
}
return $user;
}
}
很简单,我的问题是 - $user
对象的 name
属性是如何改变的?
您似乎遇到的问题是默认情况下对象是通过引用传递的,因此当您更新一个对象时,您会更新所有对象。
解决此问题的方法包括(但可能不限于):
- 使用克隆(虽然这只是一个浅拷贝,所以要小心)
- 将对象序列化然后反序列化为新变量