测试学说刷新实体

时间:2015-08-04 16:25:43

标签: php doctrine-orm phpunit

我想对我的服务类进行单元测试,这样可以节省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();

    }
}

2 个答案:

答案 0 :(得分:3)

您可以使用returnCallback函数处理传递的对象(Some example here)。

实际上你可以告诉persist mocked方法在对象上做一些东西,例如,一个基于你的例子的工作解决方案:

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

希望这个帮助

答案 1 :(得分:1)

有几点,主要问题首先是:如果您正在测试类Foo,那么所有其他复杂的依赖项应该是mocked,因为这样可以让您单独测试单元,因此&#39; unit&# 39;测试

如果您创建工厂类以从参数创建Person,并将其作为依赖项传递到主应用程序中的类Foo,那么在您的测试套件中,您可以传入模拟这个工厂将为您提供一个Person预先初始化的ID。更好的是,如果你想避免在被刷新之前Person已经拥有Id这一事实的错误测试结果,那么使模拟工厂返回模拟Person。然后,您可以使用回调来存储模拟的getId方法,该回调仅在调用EntityManager的flush方法的模拟时返回非null。

除此之外,我建议您稍微更改模型:如果Person始终需要User,则需要将其作为构造函数的类型提示参数,以及任何其他最初需要的依赖项。这样,您可以使用association mapping或直接通过ID抽象UserPerson之间的关系,但您的Foo班级不需要知道这

相关问题