单元测试持久层 - Symfony

M4v*_*ver 3 php phpunit symfony

我想在Symfony2中测试持久性.我想知道它是否更好的模拟实体并提供给实体经理或者它是更好的模拟实体menager并将实体传递给经理?

我的第一个选项但是实体管理器抛出异常而不是对象不是实体主义.如何在PHPUNIT中测试持久性symfony?

Jak*_*las 20

您应该编写持久层的集成测试,而不是编写单元测试.

单元测试中有一条规则" 不要嘲笑你不拥有的东西 ".

您不拥有Doctrine类和接口,并且您永远无法确定您对所模拟的接口所做的假设是否正确.即使你在编写测试时它们是正确的,你也无法确定Doctrine的行为是否随着时间而改变.

每当您使用第三方代码时,您应该为任何使用它编写集成测试.集成测试实际上将调用数据库并确保该学说按照您认为的方式工作.

这就是为什么从第三方的东西中解脱出来的好处.

在学说的情况下,这很容易.您可以做的一件事是为每个存储库引入一个接口.

interface ArticleRepository
{
    /**
     * @param int $id
     *
     * @return Article
     *
     * @throws ArticleNotFoundException
     */
    public function findById($id);
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松地模拟或存根这样的界面:

class MyServiceTest extends \PHPUnit_Framework_Test_Case
{
    public function test_it_does_something()
    {
        $article = new Article();
        $repository = $this->prophesize(ArticleRepository::class);
        $repository->findById(13)->willReturn($article);

        $myService = new MyService($repository->reveal());
        $myService->doSomething();

        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

该接口将使用doctrine实现:

use Doctrine\ORM\EntityRepository;

class DoctrineArticleRepository extends EntityRepository implement ArticleRepository
{
    public function findById($id)
    {
        // call doctrine here
    }
}
Run Code Online (Sandbox Code Playgroud)

实现就是你要编写的集成测试.在您对存储库的集成测试中,您实际上将调用数据库:

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class ArticleTest extends KernelTestCase
{
    private $em;
    private $repository;

    protected function setUp()
    {
        self::bootKernel();

        $this->em = static::$kernel->getContainer()
            ->get('doctrine')
            ->getManager();
        $this->repository = $this->em->getRepository(Article::class);
    }

    public function test_it_finds_an_article()
    {
         $this->createArticle(13);

         $article = $this->repository->findById(13);

         $this->assertInstanceOf(Article::class, $article);
         $this->assertSame(13, $article->getId());
    }

    /**
     * @expectedException ArticleNotFoundException
     */
    public function test_it_throws_an_exception_if_article_is_not_found()
    {
        $this->repository->findById(42);
    }

    protected function tearDown()
    {
        parent::tearDown();

        $this->em->close();
    }

    private function createArticle($id)
    {
        $article = new Article($id);
        $this->em->persist($article);
        $this->em->flush();
        // clear is important, otherwise you might get objects stored by doctrine in memory
        $this->em->clear();
    }
}
Run Code Online (Sandbox Code Playgroud)

在其他任何地方,您将使用将被存根(或模拟)的接口.在其他任何地方你都不会打到数据库.这使您的测试更快.

存根中的实体可以简单地创建或存根.大多数时候我创建实体对象而不是模拟它们.

  • 我甚至会更进一步,让我的ArticleRepository包装Doctrine层而不是扩展EntityRepository.否则,此类的实例化仍然无法控制,需要它们的类仍依赖于EntityManager来访问它 (4认同)
  • @noisebleed我使用相同的驱动程序.使用不同的驱动程序与模拟相同:您无法保证Doctrine的行为完全相同(如果您直接使用SQL,这将迫使您以跨平台的方式编写任何SQL查询) (3认同)