运行API端点测试时断言调用特定方法

问题描述 投票:0回答:1

堆栈版本:

  • Api平台3.2

  • Symfony 6.4

  • PhpUnit 9.5

  • PHP 8.0

为用户资源的 DELETE 端点编写测试。

调用端点时,必须软删除用户,并且必须将多个包含敏感数据的字段设置为空或用乱码覆盖。用户必须符合一些条件才可以删除。

通过查看返回的响应数据或从数据库检索用户,可以轻松检查端点是否执行预期操作。

我还被要求检查方法

NewsletterService::removeUserFromAlLists()
是否被调用。因此,我创建了一个模拟对象,并用模拟替换了测试容器中的 NewsletterService。

但是,在执行请求时,DeleteUserAction 会注入真正的 NewsletterService。为什么?

失败信息:

There was 1 failure:

1) App\Tests\Functional\Entity\UserTest::testErase()
Expectation failed for method name is "removeUserFromAlLists" when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

测试代码:

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Enum\User\UserErasedReasonsEnum;
use App\Entity\User;
use App\Factory\UserFactory;
use App\Service\NewsletterService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Zenstruck\Browser\HttpOptions;
use Zenstruck\Browser\Test\HasBrowser;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;

class UserTest extends ApiTestCase
{
    use ResetDatabase;
    use Factories;
    use HasBrowser;

    public function testErase(): void
    {
        static::bootKernel();
        $container = static::getContainer();

        $userToAuthenticate = UserFactory::createOne();
        $user = UserFactory::createOne([]);

        $newsletterServiceMock = $this->createMock(NewsletterService::class);
        $newsletterServiceMock->expects($this->once())
            ->method('removeUserFromAlLists')
            ->with('email', $user->getEmail());
        $container->set(NewsletterService::class, $newsletterServiceMock);

        $erasedDate = (new \DateTime())->setTime(0, 0);
        $erasedReason = (string)UserErasedReasonsEnum::WHATEVER;

        $this->browser()
            ->disableReboot()
            ->actingAs($userToAuthenticate->_real())
            ->delete($this->getIriFromResource($user))
            ->assertSuccessful()
        ;
    }

}

删除用户操作代码:

use App\Service\NewsletterService;
use Symfony\Bundle\SecurityBundle\Security;
use PHPUnit\Framework\MockObject\MockObject;

class DeleteAction
{
    public function __construct(
        private readonly Security $security,
        private readonly NewsletterService $newsletterService,
    ) {}

    public function __invoke(Request $request, int $id): Response
    {
        $user = $this->security->getUser();

        $this->newsletterService->removeUserFromAlLists($user->getEmail());
        // dd($this->newsletterService instanceof MockObject ? 'true' : 'false'); 
        // When not commented returns false
        
        // ...
    }
}
php symfony phpunit api-platform.com
1个回答
0
投票

问题是您在错误的容器中设置了服务的模拟。 ApiTestCase类创建的容器和Zenstruck Browser创建的容器不一样。您可以通过以下方法之一解决问题:

1。在 Zenstruck 浏览器容器中设置服务的模拟:

$this->browser()
    ->use(function () use ($newsletterServiceMock) {
        self::getContainer()->set(NewsletterService::class, $newsletterServiceMock);
    });

完整测试代码:

public function testErase(): void
{
    static::bootKernel();
    $container = static::getContainer();

    $userToAuthenticate = UserFactory::createOne();
    $user = UserFactory::createOne([]);

    $newsletterServiceMock = $this->createMock(NewsletterService::class);
    $newsletterServiceMock->expects($this->once())
        ->method('removeUserFromAlLists')
        ->with($user->getEmail());

    $this->browser()
        ->disableReboot()
        ->use(function () use ($newsletterServiceMock) {
            self::getContainer()->set(NewsletterService::class, $newsletterServiceMock);
        })
        ->actingAs($userToAuthenticate->_real())
        ->delete($this->getIriFromResource($user))
        ->assertSuccessful();
}

2。您可以使用 Symfony 提供的应用程序测试功能。在这种情况下,您可以直接在

ApiTestCase
的容器中设置服务的模拟。

public function testErase(): void
{
    $client = static::createClient();
    $container = static::getContainer();

    $userToAuthenticate = UserFactory::createOne();
    $user = UserFactory::createOne([]);

    $newsletterServiceMock = $this->createMock(NewsletterService::class);
    $newsletterServiceMock->expects($this->once())
        ->method('removeUserFromAlLists')
        ->with($user->getEmail());
    $container->set(NewsletterService::class, $newsletterServiceMock);

    $response = $client->request('DELETE', '/api/users/' . $user->getId());

    $this->assertResponseIsSuccessful();
}

注意: 您在创建模拟时也遇到错误。您仅将用户的电子邮件作为单个参数传递给

removeUserFromAlLists
函数。所以你只需要像这样设置期望:
with($user->getEmail())

$newsletterServiceMock = $this->createMock(NewsletterService::class);
$newsletterServiceMock->expects($this->once())
    ->method('removeUserFromAlLists')
    ->with($user->getEmail());
© www.soinside.com 2019 - 2024. All rights reserved.