'Symfony 5.2 mock service disappear in second request

I used Symfony 5.2 and I need to mock service method which execute send request to stripe payment system, obviously don't need to execute it when tests executing, so I faced with problem with that, when request was send more then one time

config/services_test.yaml:

App\Service\StripeService:
    public: true
    lazy: true

My test class

namespace App\Tests;

use App\Document\User;
use App\Service\StripeService;
use Doctrine\ODM\MongoDB\DocumentManager;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class WebTestCase extends SymfonyWebTestCase
{
    private ?string $accessToken;

    protected ?KernelBrowser $client;

    /**
     * {@inheritdoc}
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->client = static::createClient();

        $mockStripeService = $this->getMockBuilder(StripeService::class)
            ->disableOriginalConstructor()
            ->getMock();

        $mockStripeService
            ->expects($this->once())
            ->method('createCustomer');

        $this->getContainer()->set(StripeService::class, $mockStripeService);

When first request was executed and was called another request in the same test function mock was replaced to the original class, why ?

Mock behaviour correct, mock service replaced original

$this->postJson('/api/register', $userDetails);

Before called mock service still present in container

$this->postJson('/api/login', [
            'username' => $user->getUsername(),
            'password' => 'IncorrectPassword'
        ]
    );

but inside second request by some reason mock disapear and appear original service class



Solution 1:[1]

It's been a while since this question was asked... I had the same problem and looked here for help. Going through the Symfony's code revealed what is actually going on.

Inside the KernelBrowser there is a property called $hasPerformedRequest that is set to true once the first request was performed. When you try to perform the second request this flag causes the kernel to be rebooted. The way to work around this is to reboot the kernel yourself, set the mock you want and disable client from rebooting the kernel for your next request.

I have expanded your example in a way that would work as you wanted it.

namespace App\Tests;

use App\Document\User;
use App\Service\StripeService;
use Doctrine\ODM\MongoDB\DocumentManager;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class WebTestCase extends SymfonyWebTestCase
{
    private ?string $accessToken;

    protected ?KernelBrowser $client;

    /**
     * {@inheritdoc}
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->client = static::createClient();

        $mockStripeService = $this->getMockBuilder(StripeService::class)
            ->disableOriginalConstructor()
            ->getMock();

        $mockStripeService
            ->expects($this->once())
            ->method('createCustomer');

        $this->getContainer()->set(StripeService::class, $mockStripeService);

        $this->postJson('/api/register', $userDetails);

        // Reboot kernel manually
        $this->client->getKernel()->shutdown();
        $this->client->getKernel()->boot();
        // Prevent client from rebooting the kernel
        $this->client->disableReboot();

        // Set your mock again
        $this->getContainer()->set(StripeService::class, $mockStripeService);

        // Perform the second request
        $this->postJson('/api/login', [
            'username' => $user->getUsername(),
            'password' => 'IncorrectPassword'
        ]);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vorta