UnitTest和数据库

时间:2015-02-10 16:15:42

标签: unit-testing symfony doctrine-orm phpunit

我想为我的申请做一些单元测试。这是我第一次使用PHPUnit进行管理(以及更多的单元测试),我希望得到一些建议。

首先,让我们说我有这个班级

class LodgingManager
{
    private $session;
    private $entity_manager;

        public function __construct(Session $session, EntityManager $em)
        {
            $this->session = $session;
            $this->entity_manager = $em;
        }

        public function loadLodgingList()
        {
            //If I've already fetched lodgings from db, use the session one.
            //Everytime a lodging is added, session is refreshed
            $lodging_list = $this->session->get('lodging_list');
            if (!$lodging_list) {
                $lodging_repo = $this->entity_manager->getRepository('KoobiBookingEngineBundle:Lodging');
                $lodging_list = $lodging_repo->getAllForList();

                $this->session->set('lodging_list', $lodging_list);
            }

            return $lodging_list;
        } 
        [...]
    }

这是一个简单而愚蠢的方法,但是从这里开始对我来说似乎非常有用,因为代码并不是很复杂。每个人都可以看到我使用自定义DQL方法来帮助我检索一些实体并将它们放入会话中。

我已经创建了一个包含一些虚拟数据的测试数据库,我会测试它们。

据我所知,Doctrine的EntityManager有一个受保护的__construct()方法。这意味着您无法使用"真实" EntityManager但你需要使用模拟的。可以对Repository等进行相同的考虑。

所以我生产的代码如下

class LodgingManagerTest extends \PHPUnit_Framework_TestCase
{
    const LODGING_CLASS = 'KoobiBookingEngineBundle:Lodging';

    /** @var LodgingManager */
    protected $lodging_manager;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $em;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $repository;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $session;

    public function setUp()
    {
        $lodging_array_collection = new ArrayCollection();
        $lodging = $this->getMock('Koobi\BookingEngineBundle\Entity\Lodging');
        $lodging_array_collection->add($lodging);

        $repository = $this->getMockBuilder('Koobi\BookingEngineBundle\Repository\LodgingRepository')
            ->disableOriginalconstructor()
            ->getMock();
        $repository->expects($this->once())
            ->method('getAllForList')
            ->will($this->returnValue($lodging_array_collection));
        $this->repository = $repository;

        $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalconstructor()
            ->getMock();
        $em->expects($this->once())
            ->method('getRepository')
            ->with($this->equalTo(static::LODGING_CLASS))
            ->will($this->returnValue($repository));
        $this->em = $em;

        $this->session = new Session(new MockArraySessionStorage());

        $this->lodging_manager = $this->createLodgingManager($this->session, $this->em);
    }

    public function testLoadLodgingList()
    {
        $lodging_list = $this->lodging_manager->loadLodgingList();
        $this->assertCount(1, $lodging_list);

    }
}

每个测试对象都是一个模拟的对象(除了Lodging是一个"真正的"一个)。但对我来说,这个测试是无用的,因为我无法从数据库加载真实实体。

我完全知道这种测试很愚蠢,但我可以很容易地想象出其他需要真实对象的复杂测试。

那么,向专家用户询问您的意见是什么?我该如何进行更加相关和适当的测试呢?

2 个答案:

答案 0 :(得分:2)

你应该重构代码,将“简单,愚蠢”的dao与其他东西分开(会话管理和业务逻辑)。然后将你的测试分为2组或3组。

  1. 单元测试:与环境(也来自数据库)隔离测试组件。在这个测试中,你模拟你的数据库(entityManager,存储库,但它在你的技术中调用),你检查是否按预期调用这些模拟

  2. 数据库测试:您使用真实 entityManager,存储库和mysql / oracle /以测试您的sql / orm代码是否正常工作。您只测试数据库代码而没有任何业务逻辑或会话管理

  3. 端到端测试。您启动整个应用程序并模拟用户单击Web界面

答案 1 :(得分:1)

恕我直言,你应该这样做:

  1. 单元测试您的服务,以检查"业务逻辑"做得好,代码流程正确等等......
  2. 功能测试某些组件(自定义存储库方法或具体 服务)你做的数据库夹具(副作用是 你必须维护它们,你必须建立关系等等)
  3. 功能测试您的控制器只有最小化检查(HTTP响应代码)可能不会检查页面的DOM元素。
  4. 希望这个帮助