【发布时间】:2016-05-04 00:20:07
【问题描述】:
我想在 Symfony2 中测试持久性。我想知道它是更好的模拟实体并提供给实体经理,还是更好的模拟实体经理并将实体传递给经理?
我的第一个选项,但实体管理器抛出异常而不是对象不是实体学说。如何在 PHPUNIT 中测试持久性 symfony?
【问题讨论】:
我想在 Symfony2 中测试持久性。我想知道它是更好的模拟实体并提供给实体经理,还是更好的模拟实体经理并将实体传递给经理?
我的第一个选项,但实体管理器抛出异常而不是对象不是实体学说。如何在 PHPUNIT 中测试持久性 symfony?
【问题讨论】:
您应该为持久层编写集成测试,而不是编写单元测试。
单元测试中有一条规则“不要嘲笑你不拥有的东西”。
您不拥有 Doctrine 类或接口,并且您永远无法确定您对模拟的接口所做的假设是正确的。即使在您编写测试时它们是正确的,您也无法确定 Doctrine 的行为不会随着时间而改变。
每当您使用第 3 方代码时,您都应该为任何使用它的东西编写集成测试。集成测试实际上会调用数据库并确保学说按照您认为的方式工作。
这就是为什么最好将与第 3 方的东西分离。
就教义而言,这很容易。您可以做的其中一件事是为每个存储库引入一个接口。
interface ArticleRepository
{
/**
* @param int $id
*
* @return Article
*
* @throws ArticleNotFoundException
*/
public function findById($id);
}
您可以轻松地模拟或存根这样的接口:
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();
// ...
}
}
接口将用学说实现:
use Doctrine\ORM\EntityRepository;
class DoctrineArticleRepository extends EntityRepository implement ArticleRepository
{
public function findById($id)
{
// call doctrine here
}
}
实现是您要为其编写集成测试的目的。在存储库的集成测试中,您实际上会调用数据库:
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();
}
}
在其他任何地方,您都将使用将被存根(或模拟)的接口。在其他任何地方,您都不会访问数据库。这使您的测试速度更快。
您的存根中的实体可以简单地创建或存根。大多数时候我创建实体对象而不是模拟它们。
【讨论】: