tweakers / symfony-service-mock
代理层,允许服务通过替换内部实现为测试替身,而不影响服务容器的(即 Symfony)绑定。
Requires
- php: ^8.0
- friendsofphp/proxy-manager-lts: ^1.0.16
Requires (Dev)
- phpunit/phpunit: ^10.2
This package is not auto-updated.
Last update: 2024-09-19 12:08:58 UTC
README
简介
这个库是为与 Symfony 的服务容器一起使用而设计的,并利用Ocramius 的代理管理器来允许你配置一个“原始”的服务实现,该实现可以随时更改为“替代”实现。
从 Symfony 4.0 开始,一旦初始化了服务,就不允许更改或删除它。因此,当你需要在单元或功能测试中使用临时测试替身时,你不能简单地替换服务。
此库允许你向 Symfony 添加一个替代配置,该配置使用特殊代理替换服务,这允许你在 Symfony 初始化服务并开始将其作为相关服务中的依赖项使用后,仍然完全控制“内部”。
安装
只需将其作为开发依赖项包含即可
composer require --dev tweakers/symfony-service-mock
如何使用
在 Symfony 中配置的任何服务都可以在测试环境特定服务的 services.yaml 中重新配置。为了使此库正常运行,你应该将这些服务重新配置为装饰器。
如果你在常规配置中有一个名为 'App\TestService' 的服务,你可以这样配置它,以便允许模拟其内部行为
# In config\packages\test\services.yaml: services: _defaults: public: true autowire: true Tweakers\Test\MockableService\MockableServiceProxyFactory: ~ App\TestService_mocked: decorates: App\TestService decoration_inner_name: 'App\TestService.inner' factory: ['@Tweakers\Test\MockableService\MockableServiceProxyFactory', 'createServiceProxy'] arguments: ['@App\TestService.inner']
仅使用此测试配置,通常不会有任何行为上的差异。尽管由于代理的使用(请参阅Ocramius 的手册)或它是公开的,可能会有一些微小的变化。
但现在所有依赖于 App\TestService 的服务都将使用新配置的装饰代理。默认情况下,对于任何实际工作都会回退到原始服务。
因此,此配置允许你调整 TestService 的行为,而无需知道使用它的服务或 Symfony 是否已经初始化了它。这可以在单元测试中这样做
<?php namespace App; class TestService { private $stuff; public function getStuff() { return $this->stuff; } public function setStuff($stuff) { $this->stuff = $stuff; } }
<?php namespace App\Tests; use App\TestService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class TestServiceTest extends KernelTestCase { protected function setUp() { parent::setUp(); self::bootKernel(); } protected function tearDown() { parent::tearDown(); // Make sure the original version is restored inside the proxy self::$container->get(TestService::class)->restoreOriginalService(); } public function testServiceBehaviorCanBeChanged(): void { // Set the value of "stuff" to 39 in the original TestService $service = self::$container->get(TestService::class); $service->setStuff(39); $this->assertSame(39, $service->getStuff()); // Set our mock as alternative for this test $mock = $this->createMock(TestService::class); $mock->method('getStuff')->willReturn(42); $service->setAlternativeService($mock); $this->assertSame(42, $service->getStuff()); // Revert to original service $service->restoreOriginalService(); $this->assertSame(39, $service->getStuff()); } }
兼容性
此代码仅在 Symfony 3.4、4.1 和 4.2 上进行了测试,尽管它应该与 4.0 和更早的版本兼容。在 Symfony 4.1 中引入了TestContainer,它提供了对私有服务的访问。因此,在 Symfony 4.1 及更高版本中,可能不需要将服务定义为公开。但是,Symfony 会“内联”或完全删除未使用的私有服务(甚至在 4.1 中),因此如果将所有内容都定义为私有,测试可能会因为缺少服务而失败。为了防止内联,上面的示例创建了公共=true 的代理,但可能还需要重新定义特定的服务。