unit-testing - 单元测试和数据库

标签 unit-testing symfony doctrine-orm phpunit

我想为我的应用程序进行一些单元测试。由于这是我第一次使用 PHPUnit(以及一般意义上的单元测试)进行管理,我想得到建议。

首先,假设我有这个类(class)

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 方法来帮助我检索一些实体并将它们放入 session 中。

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

据我所知,Doctrine 的 EntityManager 有一个 protected __construct() 方法。这意味着您不能使用“真正的”EntityManager,但需要使用模拟的。对于存储库等也可以进行同样的考虑。

所以我生成的代码如下

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);

    }
}

每个测试对象都是模拟对象(住宿除外,它是“真实”对象)。但对我来说这个测试毫无用处,因为我无法从数据库加载真实的实体。

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

那么请问专家用户,您对此有何看法?我怎样才能继续进行更相关和更合适的测试?

最佳答案

您应该重构代码,将“简单、愚蠢”的 dao 与其他所有内容( session 管理和业务逻辑)分开。然后将您的测试分为 2 或 3 组。

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

  2. 数据库测试:您使用您的真实实体管理器、存储库和 mysql/oracle/whatever 来测试您的 sql/orm 代码是否正常工作。您仅测试数据库代码,没有任何业务逻辑或 session 管理

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

关于unit-testing - 单元测试和数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28436438/

相关文章:

c# - 使用 NDBUnit 框架进行单元测试

javascript - 如何像 Composer 一样自动加载 Bower 依赖项?

php - Doctrine 2 : selecting entity fields, 包括相关字段

php - 如何在自定义表单类型中生成绝对 URL?

php - Doctrine - Query One-To-Many, Unidirectional with Join Table association from inverse side

php - 我应该在获取后手动关闭数据库连接和语句的游标吗?

javascript - 让 beforeEach 仅运行其所在文件中的测试

unit-testing - 单元测试 : mock document used by an imported TypeScript dependency?

php - 迭代大量记录以在 Doctrine 中生成报告

python - 使用执行测试套件的相同 webInstance