Feature Test in Laravel 8 uses withHeader to set Host invalid?

1k Views Asked by At

Since my project has deployed multiple domain names, the API interface that needs to be tested is using the api.example.test domain name as the entrance.

Using $this->get('/v1/ping') in Feature Test will request to www.example.test, I hope to set up $this->withHeader('Host', config('domain.api_domain')) uniformly in setUp in ApiFeatureBaseTestCase to automatically request API related tests to api.example.test Go in.

However, in practice, I found that this is invalid. By tracing the code, I found two codes that may cause the invalid Host setting:

First place (Laravel):

Code in Laravel Framework src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php:503, pass the parameter $uri ='/v1/ping' into $this->prepareUrlForRequest($uri) and get a complete Url with the default Host, the return value is http://www.example.test/v1/ping.

Second place (Symfony):

In the code of Symfony HttpFoundation Component Request.php:355, the parsing in $uri will be used first The coming out host is overwritten in the Header as the default Host.

The above two codes eventually caused the Host I set by withHeader to fail. Obviously, in this code logic, Symfony HttpFoundation Component's choice of Host in conflict cannot be considered wrong, but I submitted this question issue was closed when it was given to Laravel Framework.

I don't know if this issue is a bug or feature?

Finally, I am sorry that my question has interrupted everyone's time, but if there is a conclusion on this question, please tell me what should be more appropriate?

My current approach is $this->get($this->api_base_url . '/v1/ping'), but I don’t think this is elegant

3Q!!1

Code example

// File: config/domain.php
return [
    'api_domain' => 'api.example.test',
    'web_domain' => 'www.example.test',
];

// File: routes/demo.php
Route::domain(config('domain.api_domain'))
     ->middleware(['auth:api', 'api.sign.check'])
     ->namespace($this->namespace)
     ->group(function () {
         Route::get('/v1/ping', function () {
             return 'This Api v1';
         });
     });

Route::domain(config('domain.web_domain'))
     ->middleware('web')
     ->namespace($this->namespace)
     ->group(base_path('routes/web.php'));

// File: tests/ApiFeatureBaseTestCase.php
namespace Tests;

class ApiFeatureBaseTestCase extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->withHeader('Host', config('domain.api_domain'));
    }
}

// File: tests/Feature/ApiPingTest.php
namespace Tests\Feature;

use Tests\ApiFeatureBaseTestCase;


class ApiPingTest extends ApiFeatureBaseTestCase
{
    public function testPing()
    {
       $this->get('/v1/ping');
    }
}
2

There are 2 best solutions below

1
On

Can you create a wrapper method on your ApiFeatureBaseTestCase class?

public function get($uri, array $headers = [])
{
    return parent::get(config('domain.api_domain') . $uri, $headers);
}

Then in your ApiPingTest class:

public function testPing()
{
    $this->get('/v1/ping');
}
0
On

I've encountered same problem, as we have 3 types of subdomains for application. To resolve this issue you need to change root URL domain which will be used by url generator before it will be used by TestClass:

public function setUp(): void
{
        /**
         * We need this to start test application with merchant app type.
         */

        parent::setUp();

        URL::forceRootUrl('http://subdomain.domain.test');
}

Now you can call $this->get('/v1/ping'); without specifying subdomain and it will work properly.

Sadly, but problem with MakesHttpRequests.php:503 still exists in 2023 and you can't set host header from test as you would like.

This also works in parallel tests php artisan test --parallel --processes=4