phpsymfonybehatsymfony-http-foundation

Create a JSON request with HttpFoundation to be used in a Behat test


I've got a JSON login query which works in curl (ie I'm sending the data and it finds the user). I want to create a behat test to check it, but I cannot reproduce it in my behat test, and the test fail. I made sure that the database was correctly set up (the user and their password are correctly set), but the error message tells me that the fields are missing anyway.

My curl call (that succeeds):

curl --request POST \
  --url http://localhost/login \
  --header 'content-type: application/json' \
  --data '{
  "username": "my_username",
  "password": "my_password"
}'

My loginController:

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;

class LoginController extends AbstractController
{
    #[Route('/login', name: 'app_login', methods: ['POST'])]
    public function index(#[CurrentUser] ?User $user): Response
    {
        if ($user === null) {
            return $this->json([
                'message' => 'An error occured',
                'errors' => ['missing credentials'],
            ], Response::HTTP_UNAUTHORIZED);
        }

        $token = 'RANDOM_TOKEN';

        return $this->json([
            'user' => $user->getUserIdentifier(),
            'token' => $token,
        ]);
    }
}

My behat scenario and the associated function:

    Scenario: Login successful
     Given I am an unlogged user
        And these users already exist
            | email       | username | password |
            | test@me.com | test     | test_pwd |
        When I POST to the login route with the following JSON data:
            | username | password  |
            | test     | test_pwd  |
        Then the response status code should be 200
        And the response should be valid JSON
        And the "user" key should contain "test"
        And the "token" key should not be empty
    /**
     * @When I :verb to the :route route with the following JSON data:
     */
    public function iVerbToTheRouteWithJSONData(string $verb, string $route, TableNode $table): void
    {
        Assert::count($table->getLines(), 2); // One for the header, one for the data
        $params = [];
        foreach ($table as $row) {
            Assert::isArray($row);
            $params = $row;
        }

        $encoded = json_encode($params);

        $this->response = $this->kernel->handle(
            Request::create($route, $verb, [], [], [], ['Content-Type' => 'application/json'], $encoded)
        );
    }

If I dump de $encoded it looks exactly like what I want ({"username":"test","password":"test_pwd"}).

If I dump the response's content, I have this error message, which is the one Symfony sends when the fields are missing :

{"message":"An error occured","errors":["missing credentials"]}

I feel like I'm missing a simple thing in my request creation, but I'm not able to find what.


Solution

  • In Symfony's Request::create method, headers should be specified using the correct server parameter keys. Specifically, the Content-Type header should be set as CONTENT_TYPE in the server parameters array, not Content-Type.