Description
Symfony version(s) affected
7.0.*
Description
I'm following the official documentation regarding tests and DTOs but I get this error when running the test. The code itself works, I've tried the API with Postman. But the test seemingly has a problem to map the request to the DTO for some reason. The error I get is this:
1) App\Tests\Feature\SignUp\Application\Http\Controller\Api\SignUpControllerTest::testSuccessfulSignUp
Symfony\Component\DependencyInjection\Exception\RuntimeException: Cannot autowire service "App\Feature\SignUp\Application\Transfer\ProfileTransfer": argument "$firstName" of method "__construct()" is type-hinted "string", you should configure its value explicitly.
The DTO looks like they are described in the documentation and the controller works, but the test has trouble. They are nested DTOs, I have a SignUpTransfer that then is using an UserTransfer and a ProfileTransfer.
SignUpTransfer
|--UserTransfer
|--ProfileTransfer
And use it in the controller #[MapRequestPayload] SignUpTransfer $signUpTransfer
:
#[Route(
path: '/api/sign-up',
name: 'api_sign_up',
methods: ['POST']
)]
public function signUp(
#[MapRequestPayload] SignUpTransfer $signUpTransfer
): JsonResponse|ErrorResponse {
//...
}
ProfileTransfer:
<?php
declare(strict_types=1);
namespace App\Feature\SignUp\Application\Transfer;
use Symfony\Component\Validator\Constraints as Assert;
readonly class ProfileTransfer
{
public function __construct(
#[Assert\NotBlank()]
#[Assert\Length(min: 2, max: 64)]
public string $firstName,
#[Assert\NotBlank()]
#[Assert\Length(min: 2, max: 64)]
public string $lastName
) {
}
public function getFirstName(): string
{
return $this->firstName;
}
public function getLastName(): string
{
return $this->lastName;
}
}
SignUpTransfer
<?php
declare(strict_types=1);
namespace App\Feature\SignUp\Application\Transfer;
use Symfony\Component\Validator\Constraints as Assert;
readonly class SignUpTransfer
{
public function __construct(
#[Assert\NotNull()]
#[Assert\Valid()]
public UserTransfer $user,
#[Assert\NotNull()]
#[Assert\Valid()]
public ProfileTransfer $profile
) {
}
public function getUser(): UserTransfer
{
return $this->user;
}
public function getProfile(): ProfileTransfer
{
return $this->profile;
}
}
The test code
<?php
declare(strict_types=1);
namespace App\Tests\Feature\SignUp\Application\Http\Controller\Api;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class SignUpControllerTest extends WebTestCase
{
protected function apiRequest(string $url, string $method, $data, $parameters = [], $files = [])
{
return static::createClient()->request(
method: $method,
uri: $url,
parameters: $parameters,
files: $files,
server: [
'HTTP_ACCEPT' => 'application/json',
'CONTENT_TYPE' => 'application/json'
],
content: json_encode($data, JSON_THROW_ON_ERROR),
changeHistory: true
);
}
public function testSuccessfulSignUp(): void
{
$this->apiRequest('/sign-up', 'POST', [
'user' => [
'email' => 'testmail@test.local',
'password' => 'password',
'confirmPassword' => 'password'
],
'profile' => [
'firstName' => 'John',
'lastName' => 'Doe'
]
]);
self::assertResponseStatusCodeSame(201);
self::assertResponseHeaderSame('Content-Type', 'application/json');
}
}
The code works, the problem is the test. I can call the endpoint via Postman without problems:
How to reproduce
Created a DTO with child DTOs like this structure:
SignUpTransfer
|--UserTransfer
|--ProfileTransfer
And try to call the API endpoint with the test client.
<?php
declare(strict_types=1);
namespace App\Tests\Feature\SignUp\Application\Http\Controller\Api;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class SignUpControllerTest extends WebTestCase
{
protected function apiRequest(string $url, string $method, $data, $parameters = [], $files = [])
{
return static::createClient()->request(
method: $method,
uri: $url,
parameters: $parameters,
files: $files,
server: [
'HTTP_ACCEPT' => 'application/json',
'CONTENT_TYPE' => 'application/json'
],
content: json_encode($data, JSON_THROW_ON_ERROR),
changeHistory: true
);
}
public function testSuccessfulSignUp(): void
{
$this->apiRequest('/sign-up', 'POST', [
'user' => [
'email' => 'testmail@test.local',
'password' => 'password',
'confirmPassword' => 'password'
],
'profile' => [
'firstName' => 'John',
'lastName' => 'Doe'
]
]);
self::assertResponseStatusCodeSame(201);
self::assertResponseHeaderSame('Content-Type', 'application/json');
}
}
Possible Solution
I have not yet investigated it but there must be a difference to how the real request is handled and the one from the test client.
Additional Context
No response