使用 PHPUnit 在 Symfony3 中测试 Services/Service 容器的优雅方式
Elegant way to testing the Services/Service Containers in Symfony3 with PHPUnit
我最近在学习 Symfony 3 框架和 依赖注入.
我希望你能帮我解决我对在 Symfony 3 中使用 PHPUnit 测试 Services 的方法的疑惑。我有些担心如何以正确的方式做到这一点。
让我们举个例子 服务 class:
// src/AppBundle/Services/MathService.php
namespace AppBundle\Services;
class MathService
{
public function subtract($a, $b)
{
return $a - $b;
}
}
我看到通常 Symfony 中的 UnitTest classes 测试 Controllers.
但是我可以测试什么独立的 classes,比如 Services(例如包含业务逻辑)而不是 Controllers ?
我知道至少有两种方法可以做到:
1. 创建一个测试 Class 扩展 PHPUnit_Framework_TestCase
和 create Service inside some methods or constructor in this Test Class 的对象(完全像 Symfony 文档中关于 testing)
// tests/AppBundle/Services/MathTest.php
namespace Tests\AppBundle\Services;
use AppBundle\Services\MathService;
class MathTest extends \PHPUnit_Framework_TestCase
{
protected $math;
public function __construct() {
$this->math = new MathService();
}
public function testSubtract()
{
$result = $this->math->subtract(5, 3);
$this->assertEquals(2, $result);
}
}
2. 使用依赖注入将我们的服务 class 作为 服务容器 。然后创建一个扩展 KernelTestCase
的测试 Class 以访问内核。它将使我们能够使用来自内核的容器 注入我们的服务(基于关于 testing Doctrine 的 Symfony 文档)。
服务容器配置:
# app/config/services.yml
services:
app.math:
class: AppBundle\Services\MathService
现在我们的 测试 Class 看起来像:
// tests/AppBundle/Services/MathTest.php
namespace Tests\AppBundle\Services;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class MathTest extends KernelTestCase
{
private $math;
protected function setUp()
{
self::bootKernel();
$this->math = static::$kernel
->getContainer()
->get('app.math');
}
public function testSubtract()
{
$result = $this->math->subtract(5, 3);
$this->assertEquals(2, $result);
}
}
选择这种方式是有好处的。
首先,我们可以通过 [=] 在 controllers 和 tests 中访问我们的 Service Container 53=]依赖注入。
其次 - 如果将来我们想要更改 Service class 的 location 或更改 name of class - 与 1. 案例相比 - 我们可以避免许多文件的更改,因为我们至少会更改 path/name在 services.yml
文件中。
我的问题:
还有其他方法可以在 Symfony 3 中测试服务 class 吗?应该使用哪种方式更好?
2018 年更新,带有棘手的 Symfony 3.4/4.0 解决方案。
这种做法with all its pros/cons is described in this post with code examples.
访问私有服务的最佳解决方案是添加编译器通行证,使所有服务public 用于测试。
1。更新内核
use Symfony\Component\HttpKernel\Kernel;
+use Symplify\PackageBuilder\DependencyInjection\CompilerPass\PublicForTestsCompilerPass;
final class AppKernel extends Kernel
{
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->addCompilerPass('...');
+ $containerBuilder->addCompilerPass(new PublicForTestsCompilerPass());
}
}
2。需要或创建自己的编译器通行证
其中 PublicForTestsCompilerPass
看起来像:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class PublicForTestsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $containerBuilder): void
{
if (! $this->isPHPUnit()) {
return;
}
foreach ($containerBuilder->getDefinitions() as $definition) {
$definition->setPublic(true);
}
foreach ($containerBuilder->getAliases() as $definition) {
$definition->setPublic(true);
}
}
private function isPHPUnit(): bool
{
// defined by PHPUnit
return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__');
}
}
要使用此 class,只需通过以下方式添加包:
composer require symplify/package-builder
当然,更好的方法是使用自己的 class,它可以满足您的需求(您可以迁移 Behat 进行测试等)。
那么您所有的测试都会按预期继续工作!
我最近在学习 Symfony 3 框架和 依赖注入.
我希望你能帮我解决我对在 Symfony 3 中使用 PHPUnit 测试 Services 的方法的疑惑。我有些担心如何以正确的方式做到这一点。
让我们举个例子 服务 class:
// src/AppBundle/Services/MathService.php
namespace AppBundle\Services;
class MathService
{
public function subtract($a, $b)
{
return $a - $b;
}
}
我看到通常 Symfony 中的 UnitTest classes 测试 Controllers.
但是我可以测试什么独立的 classes,比如 Services(例如包含业务逻辑)而不是 Controllers ?
我知道至少有两种方法可以做到:
1. 创建一个测试 Class 扩展 PHPUnit_Framework_TestCase
和 create Service inside some methods or constructor in this Test Class 的对象(完全像 Symfony 文档中关于 testing)
// tests/AppBundle/Services/MathTest.php
namespace Tests\AppBundle\Services;
use AppBundle\Services\MathService;
class MathTest extends \PHPUnit_Framework_TestCase
{
protected $math;
public function __construct() {
$this->math = new MathService();
}
public function testSubtract()
{
$result = $this->math->subtract(5, 3);
$this->assertEquals(2, $result);
}
}
2. 使用依赖注入将我们的服务 class 作为 服务容器 。然后创建一个扩展 KernelTestCase
的测试 Class 以访问内核。它将使我们能够使用来自内核的容器 注入我们的服务(基于关于 testing Doctrine 的 Symfony 文档)。
服务容器配置:
# app/config/services.yml
services:
app.math:
class: AppBundle\Services\MathService
现在我们的 测试 Class 看起来像:
// tests/AppBundle/Services/MathTest.php
namespace Tests\AppBundle\Services;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class MathTest extends KernelTestCase
{
private $math;
protected function setUp()
{
self::bootKernel();
$this->math = static::$kernel
->getContainer()
->get('app.math');
}
public function testSubtract()
{
$result = $this->math->subtract(5, 3);
$this->assertEquals(2, $result);
}
}
选择这种方式是有好处的。
首先,我们可以通过 [=] 在 controllers 和 tests 中访问我们的 Service Container 53=]依赖注入。
其次 - 如果将来我们想要更改 Service class 的 location 或更改 name of class - 与 1. 案例相比 - 我们可以避免许多文件的更改,因为我们至少会更改 path/name在 services.yml
文件中。
我的问题:
还有其他方法可以在 Symfony 3 中测试服务 class 吗?应该使用哪种方式更好?
2018 年更新,带有棘手的 Symfony 3.4/4.0 解决方案。
这种做法with all its pros/cons is described in this post with code examples.
访问私有服务的最佳解决方案是添加编译器通行证,使所有服务public 用于测试。
1。更新内核
use Symfony\Component\HttpKernel\Kernel;
+use Symplify\PackageBuilder\DependencyInjection\CompilerPass\PublicForTestsCompilerPass;
final class AppKernel extends Kernel
{
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->addCompilerPass('...');
+ $containerBuilder->addCompilerPass(new PublicForTestsCompilerPass());
}
}
2。需要或创建自己的编译器通行证
其中 PublicForTestsCompilerPass
看起来像:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class PublicForTestsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $containerBuilder): void
{
if (! $this->isPHPUnit()) {
return;
}
foreach ($containerBuilder->getDefinitions() as $definition) {
$definition->setPublic(true);
}
foreach ($containerBuilder->getAliases() as $definition) {
$definition->setPublic(true);
}
}
private function isPHPUnit(): bool
{
// defined by PHPUnit
return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__');
}
}
要使用此 class,只需通过以下方式添加包:
composer require symplify/package-builder
当然,更好的方法是使用自己的 class,它可以满足您的需求(您可以迁移 Behat 进行测试等)。
那么您所有的测试都会按预期继续工作!