我有一个新创建的 Symfony 7 应用程序,并且有一个名为:AuthCallbackDto 的 DTO
<?php
declare(strict_types=1);
namespace App\Application\Model\Auth;
use Symfony\Component\Validator\Constraints as Assert;
readonly class AuthCallbackDto
{
public function __construct(
#[Assert\NotBlank(message: 'The `code` value cannot be blank')]
#[Assert\Length(min: 5)]
private string $code,
#[Assert\NotBlank(message: 'The `session_state` value cannot be blank')]
#[Assert\Length(min: 5)]
private string $session_state,
) {
}
public function getCode(): string
{
return $this->code;
}
public function getSessionState(): string
{
return $this->session_state;
}
}
我在控制器中使用它,如下所示:
<?php
declare(strict_types=1);
namespace App\Application\Controller\Auth;
use App\Application\Model\Auth\AuthCallbackDto;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
use Symfony\Component\Routing\Attribute\Route;
#[AsController]
class Callback
{
#[Route('/auth/callback', name: 'auth/callback', methods: ['GET'])]
public function test(
#[MapQueryString] AuthCallbackDto $authCallbackDto,
): Response {
return new JsonResponse([
'code' => $authCallbackDto->getCode(),
'session_state' => $authCallbackDto->getSessionState()
]);
}
}
如果代码或 session_state 值不存在或 < 5 characters then an error is thrown. This is good!
http://localhost:8000/auth/callback?code=oihiohoih&session_state=34f34f34f - 有效(好) http://localhost:8000/auth/callback?code=&session_state= - 失败(好) 'http://localhost:8000/auth/callback?code=123&session_state=123 - 失败(好)
由于过于自信,我决定为 DTO 类编写一个非常小的单元测试。然而,我随后注意到当我从控制台运行 PHP Unit 时,约束属性没有被应用。测试中的异常永远不会抛出。
任何人都可以告诉我我做错了什么还是我只是假设太多?
这是我的单元测试:
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Application\Model\Auh;
use App\Application\Model\Auth\AuthCallbackDto;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class AuhCallbackDtoTest extends TestCase
{
/**
* @return array<array<int, string|bool>>
*/
public static function dtoData(): array
{
return [
[
'',
'',
true,
],
[
'someCodeABC12344',
'someSessionState',
false,
],
];
}
#[DataProvider('dtoData')]
public function testDtoCanBeCreated(string $code, string $sessionState, bool $exception): void
{
if ($exception) {
$this->expectException(\Exception::class);
}
$dto = new AuthCallbackDto($code, $sessionState);
self::assertEquals($dto->getCode(), $code);
self::assertEquals($dto->getSessionState(), $sessionState);
}
}
我尝试使用控制台调试验证规则,看起来不错:
bin/console debug:validator 'App\Application\Model\Auth\AuthCallbackDto'
App\Application\Model\Auth\AuthCallbackDto
------------------------------------------
+---------------+--------------------------------------------------+--------------------------+---------------------------------------------------------------------------------+
| Property | Name | Groups | Options |
+---------------+--------------------------------------------------+--------------------------+---------------------------------------------------------------------------------+
| code | property options | | [ |
| | | | "cascadeStrategy" => |
| | | | "None", |
| | | | "autoMappingStrategy" => |
| | | | "None", |
| | | | "traversalStrategy" => |
| | | | "None" |
| | | | ] |
| code | Symfony\Component\Validator\Constraints\NotBlank | Default, AuthCallbackDto | [ |
| | | | "allowNull" => |
| | | | false, |
| | | | "message" => "The `code` |
| | | | value cannot be blank", |
| | | | "normalizer" => |
| | | | null, |
| | | | "payload" => |
| | | | null |
| | | | ] |
| code | Symfony\Component\Validator\Constraints\Length | Default, AuthCallbackDto | [ |
| | | | "charset" => |
| | | | "UTF-8", |
| | | | "charsetMessage" => "This |
| | | | value does not match the expected {{ charset }} charset.", |
| | | | "countUnit" => |
| | | | "codepoints", |
| | | | "exactMessage" => "This |
| | | | value should have exactly {{ limit }} character.|This value should have exactly |
| | | | {{ limit }} characters.", |
| | | | "max" => |
| | | | null, |
| | | | "maxMessage" => "This value |
| | | | is too long. It should have {{ limit }} character or less.|This value is too |
| | | | long. It should have {{ limit }} characters or less.", |
| | | | "min" => 5, |
| | | | "minMessage" => "This value |
| | | | is too short. It should have {{ limit }} character or more.|This value is too |
| | | | short. It should have {{ limit }} characters or more.", |
| | | | "normalizer" => |
| | | | null, |
| | | | "payload" => |
| | | | null |
| | | | ] |
| session_state | property options | | [ |
| | | | "cascadeStrategy" => |
| | | | "None", |
| | | | "autoMappingStrategy" => |
| | | | "None", |
| | | | "traversalStrategy" => |
| | | | "None" |
| | | | ] |
| session_state | Symfony\Component\Validator\Constraints\NotBlank | Default, AuthCallbackDto | [ |
| | | | "allowNull" => |
| | | | false, |
| | | | "message" => "The |
| | | | `session_state` value cannot be blank", |
| | | | "normalizer" => |
| | | | null, |
| | | | "payload" => |
| | | | null |
| | | | ] |
| session_state | Symfony\Component\Validator\Constraints\Length | Default, AuthCallbackDto | [ |
| | | | "charset" => |
| | | | "UTF-8", |
| | | | "charsetMessage" => "This |
| | | | value does not match the expected {{ charset }} charset.", |
| | | | "countUnit" => |
| | | | "codepoints", |
| | | | "exactMessage" => "This |
| | | | value should have exactly {{ limit }} character.|This value should have exactly |
| | | | {{ limit }} characters.", |
| | | | "max" => |
| | | | null, |
| | | | "maxMessage" => "This value |
| | | | is too long. It should have {{ limit }} character or less.|This value is too |
| | | | long. It should have {{ limit }} characters or less.", |
| | | | "min" => 5, |
| | | | "minMessage" => "This value |
| | | | is too short. It should have {{ limit }} character or more.|This value is too |
| | | | short. It should have {{ limit }} characters or more.", |
| | | | "normalizer" => |
| | | | null, |
| | | | "payload" => |
| | | | null |
| | | | ] |
+---------------+--------------------------------------------------+--------------------------+---------------------------------------------------------------------------------+
我可以更新 DTO 的构造函数以进行一些标准的 Webmozart 静态断言调用,但当应用程序按预期运行时,这是不必要的。
...
public function __construct(
#[Assert\NotBlank]
#[Assert\Length(min: 5)]
private string $code,
#[Assert\NotBlank]
#[Assert\Length(min: 5)]
private string $session_state,
) {
\Webmozart\Assert\Assert::notEmpty($this->code);
\Webmozart\Assert\Assert::minLength($code, 5);
\Webmozart\Assert\Assert::notEmpty($this->session_state);
\Webmozart\Assert\Assert::minLength($this->session_state, 5);
}
...
那些
Assert
属性来自 Symfony Validator 组件。 PHP 不会自动在其上运行业务逻辑。但它确实在框架中做到了这一点。
您需要将其传递给 symfony 验证器。
但是,由于您正在测试控制器,因此您可以创建一个
WebTestCase
来代替。您可以通过发送请求并验证响应来(功能上)测试您的控制器。简而言之,就像您的应用程序是一个“黑匣子”一样运行功能测试。
有关更多信息,请参阅本章:https://symfony.com/doc/current/testing.html#write-your-first-application-test
如果你真的不想要这个,并且你真的想测试你的 Dto。您必须使用 Symfony 验证器验证您的 Dto。
class AuhCallbackDtoTest extends TestCase
{
public function testInvalidDto(): void
{
$validator = Validation::createValidator();
$dto = new AuthCallbackDto("", "");
$errors = $validator->validate($dto);
$this->assertCount(2, (array)$errors); // there are 2 errors
// You can check specifically for the error and the error message too
}
}
仅供参考,我还没有真正测试过这一点。因此 Validation::createValidator()
可能需要一些额外的配置来理解这些构造函数属性,但我并不完全确定。