【问题标题】:Symfony Functional Testing - How to mock controller injected service with request(submit)Symfony 功能测试 - 如何使用请求模拟控制器注入服务(提交)
【发布时间】:2020-03-03 01:03:24
【问题描述】:

如何在发出“请求”(表单/提交)的功能测试用例中模拟服务。在我提出请求后,我对容器所做的所有更改和模拟都会丢失。

我正在使用 Symfony 4 或 5。此处发布的代码也可以在这里找到:https://github.com/klodoma/symfony-demo

我有以下场景:

  • SomeActions 服务被注入到控制器构造函数中
  • 在功能单元测试中,我尝试模拟 SomeActions 函数以检查它们是否已执行(它会发送电子邮件或类似的东西)

我模拟服务并在单元测试中覆盖它:

$container->set('App\Model\SomeActions', $someActions);

现在在测试中我做了一个 $client->submit($form); 我知道它会终止内核。

我的问题是:如何在$client->submit($form);

之后将我的模拟 $someActions 注入容器中

下面是我添加到 symfony 演示应用程序中的示例代码 https://github.com/symfony/demo

在 services.yaml 中

App\Model\SomeActions:
    public: true

SomeController.php

<?php

namespace App\Controller;

use App\Model\SomeActions;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Controller used to send some emails
 *
 * @Route("/some")
 */
class SomeController extends AbstractController
{
    private $someActions;

    public function __construct(SomeActions $someActions)
    {
        //just dump the injected class name
        var_dump(get_class($someActions));

        $this->someActions = $someActions;
    }

    /**
     * @Route("/action", methods="GET|POST", name="some_action")
     * @param Request $request
     * @return Response
     */
    public function someAction(Request $request): Response
    {

        $this->someActions->doSomething();

        if ($request->get('send')) {
            $this->someActions->sendEmail();
        }

        return $this->render('default/someAction.html.twig', [
        ]);
    }
}

一些动作

<?php

namespace App\Model;

use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

class SomeActions
{
    private $mailer;

    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }

    public function doSomething()
    {
        echo 'doSomething';
    }

    public function sendEmail()
    {
        echo 'sendEmail';

        $email = (new Email())
            ->from('hello@example.com')
            ->to('you@example.com')
            ->subject('Time for Symfony Mailer!')
            ->text('Sending emails is fun again!')
            ->html('<p>See Twig integration for better HTML integration!</p>');
        $this->mailer->send($email);
    }

}

SomeControllerTest.php

<?php

namespace App\Tests\Controller;

use App\Model\SomeActions;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class SomeControllerTest extends WebTestCase
{
    public function testSomeAction()
    {
        $client = static::createClient();

        // gets the special container that allows fetching private services
        $container = self::$container;


        $someActions = $this->getMockBuilder(SomeActions::class)
            ->disableOriginalConstructor()
            ->getMock();

        //expect that sendEmail will be called
        $someActions->expects($this->once())
            ->method('sendEmail');

        //overwrite the default service: class: Mock_SomeActions_e68f817a
        $container->set('App\Model\SomeActions', $someActions);


        $crawler = $client->request('GET', '/en/some/action');

        //submit the form
        $form = $crawler->selectButton('submit')->form();

        $client->submit($form);

        //after submit the default class injected in the controller is "App\Model\SomeActions" and not the mocked service
        $response = $client->getResponse();

        $this->assertResponseIsSuccessful($response);

    }
}

【问题讨论】:

  • 这个答案对你有帮助吗? stackoverflow.com/a/19726963/3545751(注意最后一句)
  • 不是真的,有什么解决办法?
  • 这只是我的猜测 - 但您可能需要两次注入模拟服务(在获取页面之前和提交表单之前)。
  • $client-&gt;disableReboot();$client-&gt;submit($form); 如果我这样做,那么我就可以实现我想要的。不确定这是否是最好的方法。

标签: php unit-testing symfony


【解决方案1】:

解决办法是禁用内核重启:

$client->disableReboot();

如果人们深入挖掘以了解引擎盖下发生的事情,这是有道理的; 我仍然不确定是否没有更直接的答案。

public function testSomeAction()
{
    $client = static::createClient();
    $client->disableReboot();
...

【讨论】:

  • 另外,static::createClient() 之前对容器的更改似乎会丢失,因此必须在客户端创建后进行模拟。
  • 太棒了,我一直在寻找这个答案2天,非常感谢! @klodoma 你能告诉我一直使用 disableReboot 有什么缺点吗?再次感谢您!
  • @lucasfoufou 这是个好问题。在运行测试时,您需要一个“干净”的系统,例如您可以在项目设置中执行某些操作或模拟某些服务,这可能会导致不需要的结果。另一方面,就像上面的例子一样,客户端必须保持原样。不确定这是否是您问题的最佳答案,没有深入研究。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 2018-11-13
  • 1970-01-01
  • 2020-08-19
  • 2021-12-23
  • 2012-05-03
  • 1970-01-01
相关资源
最近更新 更多