我应该对扩展 Sonata Base EntityManager 类的类进行单元测试吗?
Posted
技术标签:
【中文标题】我应该对扩展 Sonata Base EntityManager 类的类进行单元测试吗?【英文标题】:Should I unit test classes which extend Sonata BaseEntityManager class? 【发布时间】:2018-07-14 12:14:21 【问题描述】:这是扩展 BaseEntityManager 的部分代码:
namespace Vop\PolicyBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Persistence\ObjectRepository;
use Sonata\CoreBundle\Model\BaseEntityManager;
class AdditionalInsuredTypeManager extends BaseEntityManager
/**
* @param int $productId
*
* @return ArrayCollection
*/
public function getProductInsuredTypes($productId = null)
$repository = $this->getRepository();
$allActiveTypes = $repository->findAllActive();
// other code
/**
* @return AdditionalInsuredTypeRepository|ObjectRepository
*/
protected function getRepository()
return parent::getRepository();
在这里我正在尝试编写一个单元测试:
public function testGetProductInsuredTypes()
$managerRegistry = $this->getMockBuilder(\Doctrine\Common\Persistence\ManagerRegistry::class)
->getMock();
$additionalInsuredTypeManager = new AdditionalInsuredTypeManager(
AdditionalInsuredTypeManager::class,
$managerRegistry
);
$additionalInsuredTypeManager->getProductInsuredTypes(null);
有什么问题:
我在嘲笑 ManagerRegistry,但我知道我不应该嘲笑我不拥有的东西。但这是构造函数的必需参数。 我收到错误:找不到 Vop\PolicyBundle\Entity\AdditionalInsuredTypeManager 类的映射信息。请检查“auto_mapping”选项 (http://symfony.com/doc/current/reference/configuration/doctrine.html#configuration-overview) 或将捆绑包添加到原则配置中的“映射”部分。 /home/darius/phpstormProjects/vop/vendor/sonata-project/core-bundle/Model/BaseManager.php:54 /home/darius/PhpstormProjects/vop/vendor/sonata-project/core-bundle/Model/BaseManager.php:153 /home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Entity/AdditionalInsuredTypeManager.php:46 /home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Entity/AdditionalInsuredTypeManager.php:21 /home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Tests/Unit/Entity/AdditionalInsuredTypeManagerTest.php:22
我不知道如何修复这个错误,但这确实与我假设的扩展 BaseEntityManager 相关。
我看到错误是由这一行引起的:
$repository = $this->getRepository();
我什至不能从构造函数中注入存储库,因为父构造函数没有这样的参数。
关于测试的信息很少:
https://sonata-project.org/bundles/core/master/doc/reference/testing.html
【问题讨论】:
【参考方案1】:我不能告诉你测试你的存储库是否有用,我也不能告诉你你的错误,除了你很可能不应该扩展学说的实体管理器。如果有任何东西使用自定义 EntityRepository 或编写一个注入 EntityManager(或更好的 EntityRegistry)的服务:
class MyEntityManager
private $entityManager;
public function __construct(EntityManager $entityManager)
$this->entityManager = $entityManager;
public function getProductInsuredTypes($productId = null)
$repository = $this->entityManager->getRepository(Product::class);
$allActiveTypes = $repository->findAllActive();
// other code
我可以给你解释一下我是如何处理测试存储库的:
我认为单元测试,尤其是模拟测试,似乎有点浪费。它们只会将您与当前的实现联系起来,因为任何相关的东西都被模拟出来了,所以您很可能不会测试任何行为。
可能有用的是在提供真实数据库连接的情况下进行功能测试,然后对存储库执行查询以查看它是否真的返回预期结果。我通常会为更复杂的查询或使用高级学说功能(如使用自定义ResultsetMap 的本机查询)执行此操作。在这种情况下,您不会模拟 EntityManager,而是使用 Doctrine 的 TestHelper 创建内存中的 sqlite 数据库。这可能看起来像这样:
protected $entityManager;
protected function setUp()
parent::setUp();
$config = DoctrineTestHelper::createTestConfiguration();
$config->setNamingStrategy(new UnderscoreNamingStrategy());
$config->setRepositoryFactory(new RepositoryFactory());
$this->entityManager = DoctrineTestHelper::createTestEntityManager($config);
缺点是,您必须手动注册自定义类型和侦听器,这意味着行为可能与您的生产配置不同。此外,您的任务仍然是设置架构并为您的测试提供固定装置。您也很可能不会在生产中使用 SQLite,所以这是另一个偏差。好处是您不必在运行之间清理数据库,并且可以轻松地并行运行测试,而且它通常比设置完整的测试数据库更快、更容易。
最后一个选项有点接近前一个选项。您可以拥有一个测试数据库,您可以在参数、环境变量或config_test.yml
中定义它,然后您可以引导内核并从您的 DI 容器中获取实体管理器。 Symfony 的 WebTestCase 可以作为这种方法的参考。
缺点是,您必须启动内核并确保为开发、生产和测试设置单独的数据库,以确保您的测试数据不会弄乱任何东西。您还必须设置架构和固定装置,此外,您很容易遇到测试未隔离并开始变得脆弱的问题,例如当以不同的顺序运行或仅运行部分测试套件时。显然,由于这是通过您的引导应用程序进行的完整集成测试,因此与单元测试或较小的功能测试相比,性能占用明显更高,并且应用程序缓存可能会让您更加头疼。
根据经验:
我在执行最基本的查询时信任 Doctrine,因此不会测试存储库的简单查找方法。 当我编写关键查询时,我更喜欢在更高层间接测试它们,例如确保在验收测试中页面显示信息。 当我遇到存储库问题或需要在较低层进行测试时,我会先进行功能测试,然后再进行存储库集成测试。tl;博士
存储库的单元测试大多毫无意义(在我看来) 如果您需要,功能测试非常适合以合理的工作量单独测试简单查询。 集成测试可确保最类似于生产的行为,但设置和维护起来更加痛苦【讨论】:
“测试你的存储库是否有用,” - 我不是在这里测试存储库,我想测试代码对存储库的输出和与存储库的交互有什么作用。我同意使用功能或集成测试来测试存储库(现在不确定是功能而不是集成 - 他们也测试与数据库的集成,不是吗?)。我想测试这个函数如何与存储库交互 - 它是否调用存储库,它根据各种情况返回什么结果。另外,我不确定我是否正在扩展学说的实体管理器 你的问题是你的mock没有指定调用方法时应该返回什么,这意味着它将返回null
。这里失败了:github.com/sonata-project/SonataCoreBundle/blob/3.x/src/Model/…
这就是我所说的毫无意义的单元测试。您必须将 EntityManager 的行为模拟到一定程度,基本上您所做的只是试图防止您的代码被破坏,而您并不真正关心或知道它实际上会返回什么以及您的假设在哪里是错误的。这就是我的功能测试或集成测试方法发挥作用的地方。在我的功能示例中,您有一个数据库连接,但与您在应用程序中使用的原始连接相比,它有些受限。如果 Sonata 不支持 SQLite 并且您达到了这些点,它可能无法完全使用。
这就是集成示例发挥作用的时候。您基本上将有一个单独的测试配置,您可以在其中运行实际数据库的副本,然后从容器中提取该实体管理器以注入您的测试,或者您甚至可以从容器中获取您正在测试的整个服务。 $kernel = static::bootKernel()
$service = $kernel->getContainer()->get('my.entity_manager_service');` 与 WebTestCase 类似。
我什至在想 - 为什么我需要 BaseEntityManager。我也许可以只创建我的类而不扩展它,然后我可以注入我想要的任何东西并轻松模拟。我的意思是示例而不是 ManagerRegistry,我可以只注入一个存储库,模拟其方法并轻松进行单元测试。以上是关于我应该对扩展 Sonata Base EntityManager 类的类进行单元测试吗?的主要内容,如果未能解决你的问题,请参考以下文章
Sonata-Project 的 Google Authenticator:来自 Application\Sonata\UserBundle\Entity\User 的未定义方法“getTwoStep
关联 \Entity\Specialists#images 是指不存在的拥有方字段 Application\Sonata\MediaBundle\Entity\Media#spec