Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

DTOs passed to controller actions are not mapped correctly in tests #54570

Copy link
Copy link
Closed
@floriankraemer

Description

@floriankraemer
Issue body actions

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:

enter image description here

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.