HTTP Tests

HTTP Tests

Introduction

Laravel provides a very fluent API for making HTTP requests to your application and examining the responses. For example, take a look at the feature test defined below:

Pest PHPUnit

<?php

test('the application returns a successful response', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});

<?php

test('the application returns a successful response', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

The get method makes a GET request into the application, while the assertStatus method asserts that the returned response should have the given HTTP status code. In addition to this simple assertion, Laravel also contains a variety of assertions for inspecting the response headers, content, JSON structure, and more.

Making Requests

To make a request to your application, you may invoke the get, post, put, patch, or delete methods within your test. These methods do not actually issue a “real” HTTP request to your application. Instead, the entire network request is simulated internally.

Instead of returning an Illuminate\Http\Response instance, test request methods return an instance of Illuminate\Testing\TestResponse, which provides a variety of helpful assertions that allow you to inspect your application’s responses:

Pest PHPUnit

<?php

test('basic request', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});

<?php

test('basic request', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_a_basic_request(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_a_basic_request(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method.

For convenience, the CSRF middleware is automatically disabled when running tests.

Customizing Request Headers

You may use the withHeaders method to customize the request’s headers before it is sent to the application. This method allows you to add any custom headers you would like to the request:

Pest PHPUnit

<?php

test('interacting with headers', function () {
    $response = $this->withHeaders([
        'X-Header' => 'Value',
    ])->post('/user', ['name' => 'Sally']);

    $response->assertStatus(201);
});

<?php

test('interacting with headers', function () {
    $response = $this->withHeaders([
        'X-Header' => 'Value',
    ])->post('/user', ['name' => 'Sally']);

    $response->assertStatus(201);
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_interacting_with_headers(): void
    {
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->post('/user', ['name' => 'Sally']);

        $response->assertStatus(201);
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_interacting_with_headers(): void
    {
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->post('/user', ['name' => 'Sally']);

        $response->assertStatus(201);
    }
}

Cookies

You may use the withCookie or withCookies methods to set cookie values before making a request. The withCookie method accepts a cookie name and value as its two arguments, while the withCookies method accepts an array of name / value pairs:

Pest PHPUnit

<?php

test('interacting with cookies', function () {
    $response = $this->withCookie('color', 'blue')->get('/');

    $response = $this->withCookies([
        'color' => 'blue',
        'name' => 'Taylor',
    ])->get('/');

    //
});

<?php

test('interacting with cookies', function () {
    $response = $this->withCookie('color', 'blue')->get('/');

    $response = $this->withCookies([
        'color' => 'blue',
        'name' => 'Taylor',
    ])->get('/');

    //
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_cookies(): void
    {
        $response = $this->withCookie('color', 'blue')->get('/');

        $response = $this->withCookies([
            'color' => 'blue',
            'name' => 'Taylor',
        ])->get('/');

        //
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_cookies(): void
    {
        $response = $this->withCookie('color', 'blue')->get('/');

        $response = $this->withCookies([
            'color' => 'blue',
            'name' => 'Taylor',
        ])->get('/');

        //
    }
}

Session / Authentication

Laravel provides several helpers for interacting with the session during HTTP testing. First, you may set the session data to a given array using the withSession method. This is useful for loading the session with data before issuing a request to your application:

Pest PHPUnit

<?php

test('interacting with the session', function () {
    $response = $this->withSession(['banned' => false])->get('/');

    //
});

<?php

test('interacting with the session', function () {
    $response = $this->withSession(['banned' => false])->get('/');

    //
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_the_session(): void
    {
        $response = $this->withSession(['banned' => false])->get('/');

        //
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_the_session(): void
    {
        $response = $this->withSession(['banned' => false])->get('/');

        //
    }
}

Laravel’s session is typically used to maintain state for the currently authenticated user. Therefore, the actingAs helper method provides a simple way to authenticate a given user as the current user. For example, we may use a model factory to generate and authenticate a user:

Pest PHPUnit

<?php

use App\Models\User;

test('an action that requires authentication', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->withSession(['banned' => false])
        ->get('/');

    //
});

<?php

use App\Models\User;

test('an action that requires authentication', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->withSession(['banned' => false])
        ->get('/');

    //
});
``` ```
<?php

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_an_action_that_requires_authentication(): void
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)
            ->withSession(['banned' => false])
            ->get('/');

        //
    }
}

<?php

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_an_action_that_requires_authentication(): void
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)
            ->withSession(['banned' => false])
            ->get('/');

        //
    }
}

You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the actingAs method. The guard that is provided to the actingAs method will also become the default guard for the duration of the test:

$this->actingAs($user, 'web')

$this->actingAs($user, 'web')

Debugging Responses

After making a test request to your application, the dump, dumpHeaders, and dumpSession methods may be used to examine and debug the response contents:

Pest PHPUnit

<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->dumpHeaders();

    $response->dumpSession();

    $response->dump();
});

<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->dumpHeaders();

    $response->dumpSession();

    $response->dump();
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->dumpHeaders();

        $response->dumpSession();

        $response->dump();
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->dumpHeaders();

        $response->dumpSession();

        $response->dump();
    }
}

Alternatively, you may use the dd, ddHeaders, ddSession, and ddJson methods to dump information about the response and then stop execution:

Pest PHPUnit

<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->ddHeaders();
    $response->ddSession();
    $response->ddJson();
    $response->dd();
});

<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->ddHeaders();
    $response->ddSession();
    $response->ddJson();
    $response->dd();
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->ddHeaders();

        $response->ddSession();

        $response->dd();
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->ddHeaders();

        $response->ddSession();

        $response->dd();
    }
}

Exception Handling

Sometimes you may need to test that your application is throwing a specific exception. To accomplish this, you may “fake” the exception handler via the Exceptions facade. Once the exception handler has been faked, you may utilize the assertReported and assertNotReported methods to make assertions against exceptions that were thrown during the request:

Pest PHPUnit

<?php

use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;

test('exception is thrown', function () {
    Exceptions::fake();

    $response = $this->get('/order/1');

    // Assert an exception was thrown...
    Exceptions::assertReported(InvalidOrderException::class);

    // Assert against the exception...
    Exceptions::assertReported(function (InvalidOrderException $e) {
        return $e->getMessage() === 'The order was invalid.';
    });
});

<?php

use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;

test('exception is thrown', function () {
    Exceptions::fake();

    $response = $this->get('/order/1');

    // Assert an exception was thrown...
    Exceptions::assertReported(InvalidOrderException::class);

    // Assert against the exception...
    Exceptions::assertReported(function (InvalidOrderException $e) {
        return $e->getMessage() === 'The order was invalid.';
    });
});
``` ```
<?php

namespace Tests\Feature;

use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_exception_is_thrown(): void
    {
        Exceptions::fake();

        $response = $this->get('/');

        // Assert an exception was thrown...
        Exceptions::assertReported(InvalidOrderException::class);

        // Assert against the exception...
        Exceptions::assertReported(function (InvalidOrderException $e) {
            return $e->getMessage() === 'The order was invalid.';
        });
    }
}

<?php

namespace Tests\Feature;

use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_exception_is_thrown(): void
    {
        Exceptions::fake();

        $response = $this->get('/');

        // Assert an exception was thrown...
        Exceptions::assertReported(InvalidOrderException::class);

        // Assert against the exception...
        Exceptions::assertReported(function (InvalidOrderException $e) {
            return $e->getMessage() === 'The order was invalid.';
        });
    }
}

The assertNotReported and assertNothingReported methods may be used to assert that a given exception was not thrown during the request or that no exceptions were thrown:

Exceptions::assertNotReported(InvalidOrderException::class);

Exceptions::assertNothingReported();

Exceptions::assertNotReported(InvalidOrderException::class);

Exceptions::assertNothingReported();

You may totally disable exception handling for a given request by invoking the withoutExceptionHandling method before making your request:

$response = $this->withoutExceptionHandling()->get('/');

$response = $this->withoutExceptionHandling()->get('/');

In addition, if you would like to ensure that your application is not utilizing features that have been deprecated by the PHP language or the libraries your application is using, you may invoke the withoutDeprecationHandling method before making your request. When deprecation handling is disabled, deprecation warnings will be converted to exceptions, thus causing your test to fail:

$response = $this->withoutDeprecationHandling()->get('/');

$response = $this->withoutDeprecationHandling()->get('/');

The assertThrows method may be used to assert that code within a given closure throws an exception of the specified type:

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

If you would like to inspect and make assertions against the exception that is thrown, you may provide a closure as the second argument to the assertThrows method:

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123;
);

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123;
);

Testing JSON APIs

Laravel also provides several helpers for testing JSON APIs and their responses. For example, the json, getJson, postJson, putJson, patchJson, deleteJson, and optionsJson methods may be used to issue JSON requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let’s write a test to make a POST request to /api/user and assert that the expected JSON data was returned:

Pest PHPUnit

<?php

test('making an api request', function () {
    $response = $this->postJson('/api/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJson([
            'created' => true,
        ]);
});

<?php

test('making an api request', function () {
    $response = $this->postJson('/api/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJson([
            'created' => true,
        ]);
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_making_an_api_request(): void
    {
        $response = $this->postJson('/api/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_making_an_api_request(): void
    {
        $response = $this->postJson('/api/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}

In addition, JSON response data may be accessed as array variables on the response, making it convenient for you to inspect the individual values returned within a JSON response:

Pest PHPUnit

expect($response['created'])->toBeTrue();

expect($response['created'])->toBeTrue();
``` ```
$this->assertTrue($response['created']);

$this->assertTrue($response['created']);

The assertJson method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present.

Asserting Exact JSON Matches

As previously mentioned, the assertJson method may be used to assert that a fragment of JSON exists within the JSON response. If you would like to verify that a given array exactly matches the JSON returned by your application, you should use the assertExactJson method:

Pest PHPUnit

<?php

test('asserting an exact json match', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertExactJson([
            'created' => true,
        ]);
});

<?php

test('asserting an exact json match', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertExactJson([
            'created' => true,
        ]);
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_an_exact_json_match(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertExactJson([
                'created' => true,
            ]);
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_an_exact_json_match(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertExactJson([
                'created' => true,
            ]);
    }
}

Asserting on JSON Paths

If you would like to verify that the JSON response contains the given data at a specified path, you should use the assertJsonPath method:

Pest PHPUnit

<?php

test('asserting a json path value', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJsonPath('team.owner.name', 'Darian');
});

<?php

test('asserting a json path value', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJsonPath('team.owner.name', 'Darian');
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_a_json_paths_value(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJsonPath('team.owner.name', 'Darian');
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_a_json_paths_value(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJsonPath('team.owner.name', 'Darian');
    }
}

The assertJsonPath method also accepts a closure, which may be used to dynamically determine if the assertion should pass:

$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);

$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);

Fluent JSON Testing

Laravel also offers a beautiful way to fluently test your application’s JSON responses. To get started, pass a closure to the assertJson method. This closure will be invoked with an instance of Illuminate\Testing\Fluent\AssertableJson which can be used to make assertions against the JSON that was returned by your application. The where method may be used to make assertions against a particular attribute of the JSON, while the missing method may be used to assert that a particular attribute is missing from the JSON:

Pest PHPUnit

use Illuminate\Testing\Fluent\AssertableJson;

test('fluent json', function () {
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
});

use Illuminate\Testing\Fluent\AssertableJson;

test('fluent json', function () {
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
});
``` ```
use Illuminate\Testing\Fluent\AssertableJson;

/**
 * A basic functional test example.
 */
public function test_fluent_json(): void
{
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
}

use Illuminate\Testing\Fluent\AssertableJson;

/**
 * A basic functional test example.
 */
public function test_fluent_json(): void
{
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
}

Understanding the etc Method

In the example above, you may have noticed we invoked the etc method at the end of our assertion chain. This method informs Laravel that there may be other attributes present on the JSON object. If the etc method is not used, the test will fail if other attributes that you did not make assertions against exist on the JSON object.

The intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the etc method.

However, you should be aware that not including the etc method in your assertion chain does not ensure that additional attributes are not being added to arrays that are nested within your JSON object. The etc method only ensures that no additional attributes exist at the nesting level in which the etc method is invoked.

Asserting Attribute Presence / Absence

To assert that an attribute is present or absent, you may use the has and missing methods:

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
        ->missing('message')
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
        ->missing('message')
);

In addition, the hasAll and missingAll methods allow asserting the presence or absence of multiple attributes simultaneously:

$response->assertJson(fn (AssertableJson $json) =>
    $json->hasAll(['status', 'data'])
        ->missingAll(['message', 'code'])
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->hasAll(['status', 'data'])
        ->missingAll(['message', 'code'])
);

You may use the hasAny method to determine if at least one of a given list of attributes is present:

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('status')
        ->hasAny('data', 'message', 'code')
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('status')
        ->hasAny('data', 'message', 'code')
);

Asserting Against JSON Collections

Often, your route will return a JSON response that contains multiple items, such as multiple users:

Route::get('/users', function () {
    return User::all();
});

Route::get('/users', function () {
    return User::all();
});

In these situations, we may use the fluent JSON object’s has method to make assertions against the users included in the response. For example, let’s assert that the JSON response contains three users. Next, we’ll make some assertions about the first user in the collection using the first method. The first method accepts a closure which receives another assertable JSON string that we can use to make assertions about the first object in the JSON collection:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
            ->first(fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
            ->first(fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

Scoping JSON Collection Assertions

Sometimes, your application’s routes will return JSON collections that are assigned named keys:

Route::get('/users', function () {
    return [
        'meta' => [...],
        'users' => User::all(),
    ];
})

Route::get('/users', function () {
    return [
        'meta' => [...],
        'users' => User::all(),
    ];
})

When testing these routes, you may use the has method to assert against the number of items in the collection. In addition, you may use the has method to scope a chain of assertions:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3)
            ->has('users.0', fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3)
            ->has('users.0', fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

However, instead of making two separate calls to the has method to assert against the users collection, you may make a single call which provides a closure as its third parameter. When doing so, the closure will automatically be invoked and scoped to the first item in the collection:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3, fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3, fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

Asserting JSON Types

You may only want to assert that the properties in the JSON response are of a certain type. The Illuminate\Testing\Fluent\AssertableJson class provides the whereType and whereAllType methods for doing just that:

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('id', 'integer')
        ->whereAllType([
            'users.0.name' => 'string',
            'meta' => 'array'
        ])
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('id', 'integer')
        ->whereAllType([
            'users.0.name' => 'string',
            'meta' => 'array'
        ])
);

You may specify multiple types using the | character, or passing an array of types as the second parameter to the whereType method. The assertion will be successful if the response value is any of the listed types:

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('name', 'string|null')
        ->whereType('id', ['string', 'integer'])
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('name', 'string|null')
        ->whereType('id', ['string', 'integer'])
);

The whereType and whereAllType methods recognize the following types: string, integer, double, boolean, array, and null.

Testing File Uploads

The Illuminate\Http\UploadedFile class provides a fake method which may be used to generate dummy files or images for testing. This, combined with the Storage facade’s fake method, greatly simplifies the testing of file uploads. For example, you may combine these two features to easily test an avatar upload form:

Pest PHPUnit

<?php

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('avatars can be uploaded', function () {
    Storage::fake('avatars');

    $file = UploadedFile::fake()->image('avatar.jpg');

    $response = $this->post('/avatar', [
        'avatar' => $file,
    ]);

    Storage::disk('avatars')->assertExists($file->hashName());
});

<?php

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('avatars can be uploaded', function () {
    Storage::fake('avatars');

    $file = UploadedFile::fake()->image('avatar.jpg');

    $response = $this->post('/avatar', [
        'avatar' => $file,
    ]);

    Storage::disk('avatars')->assertExists($file->hashName());
});
``` ```
<?php

namespace Tests\Feature;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_avatars_can_be_uploaded(): void
    {
        Storage::fake('avatars');

        $file = UploadedFile::fake()->image('avatar.jpg');

        $response = $this->post('/avatar', [
            'avatar' => $file,
        ]);

        Storage::disk('avatars')->assertExists($file->hashName());
    }
}

<?php

namespace Tests\Feature;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_avatars_can_be_uploaded(): void
    {
        Storage::fake('avatars');

        $file = UploadedFile::fake()->image('avatar.jpg');

        $response = $this->post('/avatar', [
            'avatar' => $file,
        ]);

        Storage::disk('avatars')->assertExists($file->hashName());
    }
}

If you would like to assert that a given file does not exist, you may use the assertMissing method provided by the Storage facade:

Storage::fake('avatars');

// ...

Storage::disk('avatars')->assertMissing('missing.jpg');

Storage::fake('avatars');

// ...

Storage::disk('avatars')->assertMissing('missing.jpg');

Fake File Customization

When creating files using the fake method provided by the UploadedFile class, you may specify the width, height, and size of the image (in kilobytes) in order to better test your application’s validation rules:

UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

In addition to creating images, you may create files of any other type using the create method:

UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

If needed, you may pass a $mimeType argument to the method to explicitly define the MIME type that should be returned by the file:

UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);

UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);

Testing Views

Laravel also allows you to render a view without making a simulated HTTP request to the application. To accomplish this, you may call the view method within your test. The view method accepts the view name and an optional array of data. The method returns an instance of Illuminate\Testing\TestView, which offers several methods to conveniently make assertions about the view’s contents:

Pest PHPUnit

<?php

test('a welcome view can be rendered', function () {
    $view = $this->view('welcome', ['name' => 'Taylor']);

    $view->assertSee('Taylor');
});

<?php

test('a welcome view can be rendered', function () {
    $view = $this->view('welcome', ['name' => 'Taylor']);

    $view->assertSee('Taylor');
});
``` ```
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_a_welcome_view_can_be_rendered(): void
    {
        $view = $this->view('welcome', ['name' => 'Taylor']);

        $view->assertSee('Taylor');
    }
}

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_a_welcome_view_can_be_rendered(): void
    {
        $view = $this->view('welcome', ['name' => 'Taylor']);

        $view->assertSee('Taylor');
    }
}

The TestView class provides the following assertion methods: assertSee, assertSeeInOrder, assertSeeText, assertSeeTextInOrder, assertDontSee, and assertDontSeeText.

If needed, you may get the raw, rendered view contents by casting the TestView instance to a string:

$contents = (string) $this->view('welcome');

$contents = (string) $this->view('welcome');

Sharing Errors

Some views may depend on errors shared in the global error bag provided by Laravel. To hydrate the error bag with error messages, you may use the withViewErrors method:

$view = $this->withViewErrors([
    'name' => ['Please provide a valid name.']
])->view('form');

$view->assertSee('Please provide a valid name.');

$view = $this->withViewErrors([
    'name' => ['Please provide a valid name.']
])->view('form');

$view->assertSee('Please provide a valid name.');

Rendering Blade and Components

If necessary, you may use the blade method to evaluate and render a raw Blade string. Like the view method, the blade method returns an instance of Illuminate\Testing\TestView:

$view = $this->blade(
    '<x-component :name="$name" />',
    ['name' => 'Taylor']
);

$view->assertSee('Taylor');

$view = $this->blade(
    '<x-component :name="$name" />',
    ['name' => 'Taylor']
);

$view->assertSee('Taylor');

You may use the component method to evaluate and render a Blade component. The component method returns an instance of Illuminate\Testing\TestComponent:

$view = $this->component(Profile::class, ['name' => 'Taylor']);

$view->assertSee('Taylor');

$view = $this->component(Profile::class, ['name' => 'Taylor']);

$view->assertSee('Taylor');

Available Assertions

(本文超出站点单文档体积上限,已按小节截断)