测试 Doctrine 实体
Test Doctrine flushed entity
我想对我的服务 class 进行单元测试,它可以节省 2 个实体。
只有测试失败,因为 person 实体没有从 entityManager 获得 ID,因为它被模拟了。
有没有办法在第一次调用 flush 后更新 person 对象。
class Foo
{
...
public function save()
{
$em = $this->getEntityManager();
$person = new Person();
$person->setName('Dude');
$em->persist($person);
$em->flush();
$user = new User();
$user->setPersonId($person->getId());
$user->setEmail('dude@example.com');
$em->persist($user);
$em->flush();
}
}
class FooTest
{
...
public function testSave_UserIsSaved()
{
$person = new Person();
$person->setName('dude');
$user = new User();
$user->setPersonId(4); // <-- this is where it gets wrong
$user->setEmail('dude@example.com');
$person = array(
'name' => 'Dude',
);
$user = array(
'person_id' => 3,
'email' => 'dude@example.com',
);
$emMock = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
->setMethods(array('persist', 'flush'))
->getMock();
$emMock->expects($this->exactly(2))
->method('persist')
->with(
$this->logicalOr(
$this->equalTo($person),
$this->equalTo($user)
)
);
$emMock->expects($this->exactly(2))
->method('flush');
$foo = new Foo($emMock);
$foo->save();
}
}
几点,首先是主要问题:如果您正在测试 class Foo
那么所有其他复杂的依赖项都应该是 mocked 因为这允许您单独测试单元因此'unit' 测试。
如果您创建工厂 class 以从参数创建 Person
,并将其作为依赖项传递给主应用程序中的 class Foo
,则在你的测试套件你可以传入这个工厂的模拟,它会给你一个 Person
和一个预初始化的 Id。更好的是,如果你想避免错误的测试结果,因为 Person
在被刷新之前已经有一个 Id,将模拟工厂 return 变成模拟 Person
。然后,您可以使用回调对模拟的 getId 方法进行存根,当调用 EntityManager
的 flush 方法的模拟时,该回调只会 return 非空。
除此之外,我建议您稍微更改模型:如果 User
始终需要 Person
,则需要将其作为构造函数的类型提示参数,以及任何其他最初需要的依赖项。这样,您可以使用 association mapping 或直接通过 Id 来抽象 User
和 Person
之间的关系,但 您的 Foo
class 不会不需要知道哪个
您可以使用returnCallback
函数作用于传递的对象(Some example here)。
实际上你可以告诉 persist 模拟方法在对象上做一些事情,例如,一个基于你的例子的工作解决方案:
class FooTest extends \PHPUnit_Framework_TestCase {
public function testSave_UserIsSaved()
{
$person = new Person();
$person->setName('Dude');
$user = new User();
$user->setPersonId(4); // <-- this is where it gets wrong
$user->setEmail('dude@example.com');
$emMock = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
->setMethods(array('persist', 'flush'))
->disableOriginalConstructor()
->getMock()
;
$emMock->expects($this->exactly(2))
->method('persist')
->with(
$this->logicalOr(
$this->equalTo($person),
$this->equalTo($user)
)
)
->will($this->returnCallback(function($o) {
if ($o instanceof \Acme\DemoBundle\Model\Person){
$o->setId(4);
}
}));
$emMock->expects($this->exactly(2))
->method('flush');
$foo = new Foo($emMock);
$foo->save();
}
已使用 PHPUnit 4.3.5 进行测试,一些提示请查看 this SO Question
希望对您有所帮助
我想对我的服务 class 进行单元测试,它可以节省 2 个实体。 只有测试失败,因为 person 实体没有从 entityManager 获得 ID,因为它被模拟了。
有没有办法在第一次调用 flush 后更新 person 对象。
class Foo
{
...
public function save()
{
$em = $this->getEntityManager();
$person = new Person();
$person->setName('Dude');
$em->persist($person);
$em->flush();
$user = new User();
$user->setPersonId($person->getId());
$user->setEmail('dude@example.com');
$em->persist($user);
$em->flush();
}
}
class FooTest
{
...
public function testSave_UserIsSaved()
{
$person = new Person();
$person->setName('dude');
$user = new User();
$user->setPersonId(4); // <-- this is where it gets wrong
$user->setEmail('dude@example.com');
$person = array(
'name' => 'Dude',
);
$user = array(
'person_id' => 3,
'email' => 'dude@example.com',
);
$emMock = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
->setMethods(array('persist', 'flush'))
->getMock();
$emMock->expects($this->exactly(2))
->method('persist')
->with(
$this->logicalOr(
$this->equalTo($person),
$this->equalTo($user)
)
);
$emMock->expects($this->exactly(2))
->method('flush');
$foo = new Foo($emMock);
$foo->save();
}
}
几点,首先是主要问题:如果您正在测试 class Foo
那么所有其他复杂的依赖项都应该是 mocked 因为这允许您单独测试单元因此'unit' 测试。
如果您创建工厂 class 以从参数创建 Person
,并将其作为依赖项传递给主应用程序中的 class Foo
,则在你的测试套件你可以传入这个工厂的模拟,它会给你一个 Person
和一个预初始化的 Id。更好的是,如果你想避免错误的测试结果,因为 Person
在被刷新之前已经有一个 Id,将模拟工厂 return 变成模拟 Person
。然后,您可以使用回调对模拟的 getId 方法进行存根,当调用 EntityManager
的 flush 方法的模拟时,该回调只会 return 非空。
除此之外,我建议您稍微更改模型:如果 User
始终需要 Person
,则需要将其作为构造函数的类型提示参数,以及任何其他最初需要的依赖项。这样,您可以使用 association mapping 或直接通过 Id 来抽象 User
和 Person
之间的关系,但 您的 Foo
class 不会不需要知道哪个
您可以使用returnCallback
函数作用于传递的对象(Some example here)。
实际上你可以告诉 persist 模拟方法在对象上做一些事情,例如,一个基于你的例子的工作解决方案:
class FooTest extends \PHPUnit_Framework_TestCase {
public function testSave_UserIsSaved()
{
$person = new Person();
$person->setName('Dude');
$user = new User();
$user->setPersonId(4); // <-- this is where it gets wrong
$user->setEmail('dude@example.com');
$emMock = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
->setMethods(array('persist', 'flush'))
->disableOriginalConstructor()
->getMock()
;
$emMock->expects($this->exactly(2))
->method('persist')
->with(
$this->logicalOr(
$this->equalTo($person),
$this->equalTo($user)
)
)
->will($this->returnCallback(function($o) {
if ($o instanceof \Acme\DemoBundle\Model\Person){
$o->setId(4);
}
}));
$emMock->expects($this->exactly(2))
->method('flush');
$foo = new Foo($emMock);
$foo->save();
}
已使用 PHPUnit 4.3.5 进行测试,一些提示请查看 this SO Question
希望对您有所帮助