diff --git a/billing.md b/billing.md
index b828e969d4f..29af60aefa0 100644
--- a/billing.md
+++ b/billing.md
@@ -2288,7 +2288,7 @@ You may invoke the `stripe` method on the `Cashier` class if you would like to u
## Testing
-When testing an application that uses Cashier, you may mock the actual HTTP requests to the Stripe API; however, this requires you to partially re-implement Cashier's own behavior. Therefore, we recommend allowing your tests to hit the actual Stripe API. While this is slower, it provides more confidence that your application is working as expected and any slow tests may be placed within their own PHPUnit testing group.
+When testing an application that uses Cashier, you may mock the actual HTTP requests to the Stripe API; however, this requires you to partially re-implement Cashier's own behavior. Therefore, we recommend allowing your tests to hit the actual Stripe API. While this is slower, it provides more confidence that your application is working as expected and any slow tests may be placed within their own Pest / PHPUnit testing group.
When testing, remember that Cashier itself already has a great test suite, so you should only focus on testing the subscription and payment flow of your own application and not every underlying Cashier behavior.
diff --git a/console-tests.md b/console-tests.md
index 587ef58fb98..98c8f59a601 100644
--- a/console-tests.md
+++ b/console-tests.md
@@ -15,13 +15,21 @@ In addition to simplifying HTTP testing, Laravel provides a simple API for testi
To get started, let's explore how to make assertions regarding an Artisan command's exit code. To accomplish this, we will use the `artisan` method to invoke an Artisan command from our test. Then, we will use the `assertExitCode` method to assert that the command completed with a given exit code:
- /**
- * Test a console command.
- */
- public function test_console_command(): void
- {
- $this->artisan('inspire')->assertExitCode(0);
- }
+```php tab=Pest
+test('console command', function () {
+ $this->artisan('inspire')->assertExitCode(0);
+});
+```
+
+```php tab=PHPUnit
+/**
+ * Test a console command.
+ */
+public function test_console_command(): void
+{
+ $this->artisan('inspire')->assertExitCode(0);
+}
+```
You may use the `assertNotExitCode` method to assert that the command did not exit with a given exit code:
@@ -52,20 +60,35 @@ Laravel allows you to easily "mock" user input for your console commands using t
You may test this command with the following test which utilizes the `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, `expectsOutputToContain`, `doesntExpectOutputToContain`, and `assertExitCode` methods:
- /**
- * Test a console command.
- */
- public function test_console_command(): void
- {
- $this->artisan('question')
- ->expectsQuestion('What is your name?', 'Taylor Otwell')
- ->expectsQuestion('Which language do you prefer?', 'PHP')
- ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')
- ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')
- ->expectsOutputToContain('Taylor Otwell')
- ->doesntExpectOutputToContain('you prefer Ruby')
- ->assertExitCode(0);
- }
+```php tab=Pest
+test('console command', function () {
+ $this->artisan('question')
+ ->expectsQuestion('What is your name?', 'Taylor Otwell')
+ ->expectsQuestion('Which language do you prefer?', 'PHP')
+ ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')
+ ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')
+ ->expectsOutputToContain('Taylor Otwell')
+ ->doesntExpectOutputToContain('you prefer Ruby')
+ ->assertExitCode(0);
+});
+```
+
+```php tab=PHPUnit
+/**
+ * Test a console command.
+ */
+public function test_console_command(): void
+{
+ $this->artisan('question')
+ ->expectsQuestion('What is your name?', 'Taylor Otwell')
+ ->expectsQuestion('Which language do you prefer?', 'PHP')
+ ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')
+ ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')
+ ->expectsOutputToContain('Taylor Otwell')
+ ->doesntExpectOutputToContain('you prefer Ruby')
+ ->assertExitCode(0);
+}
+```
#### Confirmation Expectations
@@ -95,16 +118,28 @@ If your command displays a table of information using Artisan's `table` method,
By default, the `Illuminate\Console\Events\CommandStarting` and `Illuminate\Console\Events\CommandFinished` events are not dispatched while running your application's tests. However, you can enable these events for a given test class by adding the `Illuminate\Foundation\Testing\WithConsoleEvents` trait to the class:
- get('/');
- /**
- * A basic functional test example.
- */
- public function test_basic_example(): void
- {
- $response = $this->get('/');
+ // ...
+});
+```
- // ...
- }
+```php tab=PHPUnit
+get('/');
+
+ // ...
}
+}
+```
The `Illuminate\Foundation\Testing\RefreshDatabase` trait does not migrate your database if your schema is up to date. Instead, it will only execute the test within a database transaction. Therefore, any records added to the database by test cases that do not use this trait may still exist in the database.
@@ -49,54 +65,95 @@ When testing, you may need to insert a few records into your database before exe
To learn more about creating and utilizing model factories to create models, please consult the complete [model factory documentation](/docs/{{version}}/eloquent-factories). Once you have defined a model factory, you may utilize the factory within your test to create models:
- use App\Models\User;
+```php tab=Pest
+use App\Models\User;
- public function test_models_can_be_instantiated(): void
- {
- $user = User::factory()->create();
+test('models can be instantiated', function () {
+ $user = User::factory()->create();
- // ...
- }
+ // ...
+});
+```
+
+```php tab=PHPUnit
+use App\Models\User;
+
+public function test_models_can_be_instantiated(): void
+{
+ $user = User::factory()->create();
+
+ // ...
+}
+```
## Running Seeders
If you would like to use [database seeders](/docs/{{version}}/seeding) to populate your database during a feature test, you may invoke the `seed` method. By default, the `seed` method will execute the `DatabaseSeeder`, which should execute all of your other seeders. Alternatively, you pass a specific seeder class name to the `seed` method:
- seed();
+
+ // Run a specific seeder...
+ $this->seed(OrderStatusSeeder::class);
+
+ // ...
+
+ // Run an array of specific seeders...
+ $this->seed([
+ OrderStatusSeeder::class,
+ TransactionStatusSeeder::class,
+ // ...
+ ]);
+});
+```
- class ExampleTest extends TestCase
+```php tab=PHPUnit
+seed();
- /**
- * Test creating a new order.
- */
- public function test_orders_can_be_created(): void
- {
- // Run the DatabaseSeeder...
- $this->seed();
+ // Run a specific seeder...
+ $this->seed(OrderStatusSeeder::class);
- // Run a specific seeder...
- $this->seed(OrderStatusSeeder::class);
+ // ...
+ // Run an array of specific seeders...
+ $this->seed([
+ OrderStatusSeeder::class,
+ TransactionStatusSeeder::class,
// ...
-
- // Run an array of specific seeders...
- $this->seed([
- OrderStatusSeeder::class,
- TransactionStatusSeeder::class,
- // ...
- ]);
- }
+ ]);
}
+}
+```
Alternatively, you may instruct Laravel to automatically seed the database before each test that uses the `RefreshDatabase` trait. You may accomplish this by defining a `$seed` property on your base test class:
@@ -132,7 +189,7 @@ When the `$seed` property is `true`, the test will run the `Database\Seeders\Dat
## Available Assertions
-Laravel provides several database assertions for your [PHPUnit](https://phpunit.de/) feature tests. We'll discuss each of these assertions below.
+Laravel provides several database assertions for your [Pest](https://pestphp.com) or [PHPUnit](https://phpunit.de) feature tests. We'll discuss each of these assertions below.
#### assertDatabaseCount
diff --git a/dusk.md b/dusk.md
index e25ebd51f04..a9ec7f26565 100644
--- a/dusk.md
+++ b/dusk.md
@@ -153,19 +153,33 @@ Most of the tests you write will interact with pages that retrieve data from you
The `DatabaseMigrations` trait will run your database migrations before each test. However, dropping and re-creating your database tables for each test is typically slower than truncating the tables:
- [!WARNING]
> SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes.
@@ -175,22 +189,40 @@ The `DatabaseMigrations` trait will run your database migrations before each tes
The `DatabaseTruncation` trait will migrate your database on the first test in order to ensure your database tables have been properly created. However, on subsequent tests, the database's tables will simply be truncated - providing a speed boost over re-running all of your database migrations:
- [!NOTE]
+> If you are using Pest, you should define properties or methods on the base `DuskTestCase` class or on any class your test file extends.
+
/**
* Indicates which tables should be truncated.
*
@@ -249,7 +281,7 @@ If you had test failures the last time you ran the `dusk` command, you may save
php artisan dusk:fails
```
-The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group):
+The `dusk` command accepts any argument that is normally accepted by the Pest / PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group):
```shell
php artisan dusk --group=foo
@@ -302,38 +334,63 @@ When running tests, Dusk will back-up your `.env` file and rename your Dusk envi
To get started, let's write a test that verifies we can log into our application. After generating a test, we can modify it to navigate to the login page, enter some credentials, and click the "Login" button. To create a browser instance, you may call the `browse` method from within your Dusk test:
- create([
+ 'email' => 'taylor@laravel.com',
+ ]);
- /**
- * A basic browser test example.
- */
- public function test_basic_example(): void
- {
- $user = User::factory()->create([
- 'email' => 'taylor@laravel.com',
- ]);
-
- $this->browse(function (Browser $browser) use ($user) {
- $browser->visit('/login')
- ->type('email', $user->email)
- ->type('password', 'password')
- ->press('Login')
- ->assertPathIs('/home');
- });
- }
+ $this->browse(function (Browser $browser) use ($user) {
+ $browser->visit('/login')
+ ->type('email', $user->email)
+ ->type('password', 'password')
+ ->press('Login')
+ ->assertPathIs('/home');
+ });
+});
+```
+
+```php tab=PHPUnit
+create([
+ 'email' => 'taylor@laravel.com',
+ ]);
+
+ $this->browse(function (Browser $browser) use ($user) {
+ $browser->visit('/login')
+ ->type('email', $user->email)
+ ->type('password', 'password')
+ ->press('Login')
+ ->assertPathIs('/home');
+ });
}
+}
+```
As you can see in the example above, the `browse` method accepts a closure. A browser instance will automatically be passed to this closure by Dusk and is the main object used to interact with and make assertions against your application.
@@ -1720,16 +1777,27 @@ Dusk even allows you to make assertions on the state of [Vue component](https://
You may assert on the state of the Vue component like so:
- /**
- * A basic Vue test example.
- */
- public function test_vue(): void
- {
- $this->browse(function (Browser $browser) {
- $browser->visit('/')
- ->assertVue('user.name', 'Taylor', '@profile-component');
- });
- }
+```php tab=Pest
+test('vue', function () {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->assertVue('user.name', 'Taylor', '@profile-component');
+ });
+});
+```
+
+```php tab=PHPUnit
+/**
+ * A basic Vue test example.
+ */
+public function test_vue(): void
+{
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->assertVue('user.name', 'Taylor', '@profile-component');
+ });
+}
+```
#### assertVueIsNot
@@ -1962,31 +2030,53 @@ As shown above, a "date picker" is an example of a component that might exist th
Once the component has been defined, we can easily select a date within the date picker from any test. And, if the logic necessary to select a date changes, we only need to update the component:
- browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->within(new DatePicker, function (Browser $browser) {
+ $browser->selectDate(2019, 1, 30);
+ })
+ ->assertSee('January');
+ });
+});
+```
- class ExampleTest extends DuskTestCase
+```php tab=PHPUnit
+browse(function (Browser $browser) {
- $browser->visit('/')
- ->within(new DatePicker, function (Browser $browser) {
- $browser->selectDate(2019, 1, 30);
- })
- ->assertSee('January');
- });
- }
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->within(new DatePicker, function (Browser $browser) {
+ $browser->selectDate(2019, 1, 30);
+ })
+ ->assertSee('January');
+ });
}
+}
+```
## Continuous Integration
diff --git a/events.md b/events.md
index 68240d8f066..ff25daa197a 100644
--- a/events.md
+++ b/events.md
@@ -682,39 +682,67 @@ When testing code that dispatches events, you may wish to instruct Laravel to no
Using the `Event` facade's `fake` method, you may prevent listeners from executing, execute the code under test, and then assert which events were dispatched by your application using the `assertDispatched`, `assertNotDispatched`, and `assertNothingDispatched` methods:
- create();
+ $order = Order::factory()->create();
- Event::assertDispatched(OrderCreated::class);
+ Event::assertDispatched(OrderCreated::class);
- // Other events are dispatched as normal...
- $order->update([...]);
- }
+ // Other events are dispatched as normal...
+ $order->update([...]);
+});
+```
+
+```php tab=PHPUnit
+/**
+ * Test order process.
+ */
+public function test_orders_can_be_processed(): void
+{
+ Event::fake([
+ OrderCreated::class,
+ ]);
+
+ $order = Order::factory()->create();
+
+ Event::assertDispatched(OrderCreated::class);
+
+ // Other events are dispatched as normal...
+ $order->update([...]);
+}
+```
You may fake all events except for a set of specified events using the `except` method:
@@ -765,31 +810,54 @@ You may fake all events except for a set of specified events using the `except`
If you only want to fake event listeners for a portion of your test, you may use the `fakeFor` method:
- create();
+
+ Event::assertDispatched(OrderCreated::class);
+
+ return $order;
+ });
- class ExampleTest extends TestCase
+ // Events are dispatched as normal and observers will run ...
+ $order->update([...]);
+});
+```
+
+```php tab=PHPUnit
+create();
+ $order = Event::fakeFor(function () {
+ $order = Order::factory()->create();
- Event::assertDispatched(OrderCreated::class);
+ Event::assertDispatched(OrderCreated::class);
- return $order;
- });
+ return $order;
+ });
- // Events are dispatched as normal and observers will run ...
- $order->update([...]);
- }
+ // Events are dispatched as normal and observers will run ...
+ $order->update([...]);
}
+}
+```
diff --git a/facades.md b/facades.md
index 2774406fdba..e8ecca5e55c 100644
--- a/facades.md
+++ b/facades.md
@@ -69,21 +69,37 @@ Typically, it would not be possible to mock or stub a truly static class method.
Using Laravel's facade testing methods, we can write the following test to verify that the `Cache::get` method was called with the argument we expected:
- use Illuminate\Support\Facades\Cache;
+```php tab=Pest
+use Illuminate\Support\Facades\Cache;
- /**
- * A basic functional test example.
- */
- public function test_basic_example(): void
- {
- Cache::shouldReceive('get')
- ->with('key')
- ->andReturn('value');
+test('basic example', function () {
+ Cache::shouldReceive('get')
+ ->with('key')
+ ->andReturn('value');
- $response = $this->get('/cache');
+ $response = $this->get('/cache');
- $response->assertSee('value');
- }
+ $response->assertSee('value');
+});
+```
+
+```php tab=PHPUnit
+use Illuminate\Support\Facades\Cache;
+
+/**
+ * A basic functional test example.
+ */
+public function test_basic_example(): void
+{
+ Cache::shouldReceive('get')
+ ->with('key')
+ ->andReturn('value');
+
+ $response = $this->get('/cache');
+
+ $response->assertSee('value');
+}
+```
### Facades vs. Helper Functions
@@ -215,31 +231,51 @@ Injecting a publisher implementation into the method allows us to easily test th
When the real-time facade is used, the publisher implementation will be resolved out of the service container using the portion of the interface or class name that appears after the `Facades` prefix. When testing, we can use Laravel's built-in facade testing helpers to mock this method call:
- create();
- /**
- * A test example.
- */
- public function test_podcast_can_be_published(): void
- {
- $podcast = Podcast::factory()->create();
+ Publisher::shouldReceive('publish')->once()->with($podcast);
- Publisher::shouldReceive('publish')->once()->with($podcast);
+ $podcast->publish();
+});
+```
- $podcast->publish();
- }
+```php tab=PHPUnit
+create();
+
+ Publisher::shouldReceive('publish')->once()->with($podcast);
+
+ $podcast->publish();
}
+}
+```
## Facade Class Reference
diff --git a/filesystem.md b/filesystem.md
index 454efbc6afe..f1ac8911449 100644
--- a/filesystem.md
+++ b/filesystem.md
@@ -647,37 +647,66 @@ Finally, the `deleteDirectory` method may be used to remove a directory and all
The `Storage` facade's `fake` method allows you to easily generate a fake disk that, combined with the file generation utilities of the `Illuminate\Http\UploadedFile` class, greatly simplifies the testing of file uploads. For example:
- json('POST', '/photos', [
+ UploadedFile::fake()->image('photo1.jpg'),
+ UploadedFile::fake()->image('photo2.jpg')
+ ]);
+
+ // Assert one or more files were stored...
+ Storage::disk('photos')->assertExists('photo1.jpg');
+ Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
+
+ // Assert one or more files were not stored...
+ Storage::disk('photos')->assertMissing('missing.jpg');
+ Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
+
+ // Assert that a given directory is empty...
+ Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
+});
+```
- class ExampleTest extends TestCase
+```php tab=PHPUnit
+json('POST', '/photos', [
- UploadedFile::fake()->image('photo1.jpg'),
- UploadedFile::fake()->image('photo2.jpg')
- ]);
+ $response = $this->json('POST', '/photos', [
+ UploadedFile::fake()->image('photo1.jpg'),
+ UploadedFile::fake()->image('photo2.jpg')
+ ]);
- // Assert one or more files were stored...
- Storage::disk('photos')->assertExists('photo1.jpg');
- Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
+ // Assert one or more files were stored...
+ Storage::disk('photos')->assertExists('photo1.jpg');
+ Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
- // Assert one or more files were not stored...
- Storage::disk('photos')->assertMissing('missing.jpg');
- Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
+ // Assert one or more files were not stored...
+ Storage::disk('photos')->assertMissing('missing.jpg');
+ Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
- // Assert that a given directory is empty...
- Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
- }
+ // Assert that a given directory is empty...
+ Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
}
+}
+```
By default, the `fake` method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads).
diff --git a/helpers.md b/helpers.md
index cbd6f55aa6d..9f00477883b 100644
--- a/helpers.md
+++ b/helpers.md
@@ -2223,29 +2223,55 @@ When testing code that utilizes the `Sleep` class or PHP's native sleep function
Typically, testing this code would take _at least_ one second. Luckily, the `Sleep` class allows us to "fake" sleeping so that our test suite stays fast:
- public function test_it_waits_until_ready()
- {
- Sleep::fake();
+```php tab=Pest
+it('waits until ready', function () {
+ Sleep::fake();
- // ...
- }
+ // ...
+});
+```
+
+```php tab=PHPUnit
+public function test_it_waits_until_ready()
+{
+ Sleep::fake();
+
+ // ...
+}
+```
When faking the `Sleep` class, the actual execution pause is by-passed, leading to a substantially faster test.
Once the `Sleep` class has been faked, it is possible to make assertions against the expected "sleeps" that should have occurred. To illustrate this, let's imagine we are testing code that pauses execution three times, with each pause increasing by a single second. Using the `assertSequence` method, we can assert that our code "slept" for the proper amount of time while keeping our test fast:
- public function test_it_checks_if_ready_four_times()
- {
- Sleep::fake();
+```php tab=Pest
+it('checks if ready three times', function () {
+ Sleep::fake();
- // ...
+ // ...
- Sleep::assertSequence([
- Sleep::for(1)->second(),
- Sleep::for(2)->seconds(),
- Sleep::for(3)->seconds(),
- ]);
- }
+ Sleep::assertSequence([
+ Sleep::for(1)->second(),
+ Sleep::for(2)->seconds(),
+ Sleep::for(3)->seconds(),
+ ]);
+}
+```
+
+```php tab=PHPUnit
+public function test_it_checks_if_ready_four_times()
+{
+ Sleep::fake();
+
+ // ...
+
+ Sleep::assertSequence([
+ Sleep::for(1)->second(),
+ Sleep::for(2)->seconds(),
+ Sleep::for(3)->seconds(),
+ ]);
+}
+```
Of course, the `Sleep` class offers a variety of other assertions you may use when testing:
diff --git a/http-tests.md b/http-tests.md
index 8d577bd52d6..20c3b122ca0 100644
--- a/http-tests.md
+++ b/http-tests.md
@@ -22,7 +22,17 @@
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:
-```php
+```php tab=Pest
+get('/');
+
+ $response->assertStatus(200);
+});
+```
+
+```php tab=PHPUnit
get('/');
- namespace Tests\Feature;
+ $response->assertStatus(200);
+});
+```
+
+```php tab=PHPUnit
+get('/');
+ $response = $this->get('/');
- $response->assertStatus(200);
- }
+ $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.
-> [!NOTE]
+> [!NOTE]
> For convenience, the CSRF middleware is automatically disabled when running tests.
@@ -81,90 +103,157 @@ In general, each of your tests should only make one request to your application.
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:
- withHeaders([
+ 'X-Header' => 'Value',
+ ])->post('/user', ['name' => 'Sally']);
- namespace Tests\Feature;
+ $response->assertStatus(201);
+});
+```
+
+```php tab=PHPUnit
+withHeaders([
- 'X-Header' => 'Value',
- ])->post('/user', ['name' => 'Sally']);
+ $response = $this->withHeaders([
+ 'X-Header' => 'Value',
+ ])->post('/user', ['name' => 'Sally']);
- $response->assertStatus(201);
- }
+ $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:
- withCookie('color', 'blue')->get('/');
+
+ $response = $this->withCookies([
+ 'color' => 'blue',
+ 'name' => 'Taylor',
+ ])->get('/');
- namespace Tests\Feature;
+ //
+});
+```
- use Tests\TestCase;
+```php tab=PHPUnit
+withCookie('color', 'blue')->get('/');
+ $response = $this->withCookie('color', 'blue')->get('/');
- $response = $this->withCookies([
- 'color' => 'blue',
- 'name' => 'Taylor',
- ])->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:
- withSession(['banned' => false])->get('/');
+
+ //
+});
+```
+
+```php tab=PHPUnit
+withSession(['banned' => false])->get('/');
- }
+ $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](/docs/{{version}}/eloquent-factories) to generate and authenticate a user:
- create();
+
+ $response = $this->actingAs($user)
+ ->withSession(['banned' => false])
+ ->get('/');
+
+ //
+});
+```
+
+```php tab=PHPUnit
+create();
+ $user = User::factory()->create();
- $response = $this->actingAs($user)
- ->withSession(['banned' => false])
- ->get('/');
- }
+ $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:
@@ -175,53 +264,85 @@ You may also specify which guard should be used to authenticate the given user b
After making a test request to your application, the `dump`, `dumpHeaders`, and `dumpSession` methods may be used to examine and debug the response contents:
- get('/');
- namespace Tests\Feature;
+ $response->dumpHeaders();
- use Tests\TestCase;
+ $response->dumpSession();
+
+ $response->dump();
+});
+```
- class ExampleTest extends TestCase
+```php tab=PHPUnit
+get('/');
+ $response = $this->get('/');
- $response->dumpHeaders();
+ $response->dumpHeaders();
- $response->dumpSession();
+ $response->dumpSession();
- $response->dump();
- }
+ $response->dump();
}
+}
+```
Alternatively, you may use the `dd`, `ddHeaders`, and `ddSession` methods to dump information about the response and then stop execution:
- get('/');
+
+ $response->ddHeaders();
+
+ $response->ddSession();
- namespace Tests\Feature;
+ $response->dd();
+});
+```
+
+```php tab=PHPUnit
+get('/');
+ $response = $this->get('/');
- $response->ddHeaders();
+ $response->ddHeaders();
- $response->ddSession();
+ $response->ddSession();
- $response->dd();
- }
+ $response->dd();
}
+}
+```
### Exception Handling
@@ -248,34 +369,56 @@ $this->assertThrows(
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:
- postJson('/api/user', ['name' => 'Sally']);
+
+ $response
+ ->assertStatus(201)
+ ->assertJson([
+ 'created' => true,
+ ]);
+});
+```
+
+```php tab=PHPUnit
+postJson('/api/user', ['name' => 'Sally']);
+ $response = $this->postJson('/api/user', ['name' => 'Sally']);
- $response
- ->assertStatus(201)
- ->assertJson([
- 'created' => true,
- ]);
- }
+ $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:
- $this->assertTrue($response['created']);
+```php tab=Pest
+expect($response['created'])->toBeTrue();
+```
-> [!NOTE]
+```php tab=PHPUnit
+$this->assertTrue($response['created']);
+```
+
+> [!NOTE]
> The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` 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.
@@ -283,54 +426,85 @@ In addition, JSON response data may be accessed as array variables on the respon
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:
- postJson('/user', ['name' => 'Sally']);
- use Tests\TestCase;
+ $response
+ ->assertStatus(201)
+ ->assertExactJson([
+ 'created' => true,
+ ]);
+});
- class ExampleTest extends TestCase
+```
+
+```php tab=PHPUnit
+postJson('/user', ['name' => 'Sally']);
+ $response = $this->postJson('/user', ['name' => 'Sally']);
- $response
- ->assertStatus(201)
- ->assertExactJson([
- 'created' => true,
- ]);
- }
+ $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:
- postJson('/user', ['name' => 'Sally']);
- namespace Tests\Feature;
+ $response
+ ->assertStatus(201)
+ ->assertJsonPath('team.owner.name', 'Darian');
+});
+```
- use Tests\TestCase;
+```php tab=PHPUnit
+postJson('/user', ['name' => 'Sally']);
+ $response = $this->postJson('/user', ['name' => 'Sally']);
- $response
- ->assertStatus(201)
- ->assertJsonPath('team.owner.name', 'Darian');
- }
+ $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:
@@ -341,25 +515,45 @@ The `assertJsonPath` method also accepts a closure, which may be used to dynamic
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:
- use Illuminate\Testing\Fluent\AssertableJson;
+```php tab=Pest
+use Illuminate\Testing\Fluent\AssertableJson;
- /**
- * A basic functional test example.
- */
- public function test_fluent_json(): void
- {
- $response = $this->getJson('/users/1');
+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('victoria@gmail.com'))
- ->whereNot('status', 'pending')
- ->missing('password')
- ->etc()
- );
- }
+ $response
+ ->assertJson(fn (AssertableJson $json) =>
+ $json->where('id', 1)
+ ->where('name', 'Victoria Faith')
+ ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
+ ->whereNot('status', 'pending')
+ ->missing('password')
+ ->etc()
+ );
+});
+```
+
+```php tab=PHPUnit
+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('victoria@gmail.com'))
+ ->whereNot('status', 'pending')
+ ->missing('password')
+ ->etc()
+ );
+}
+```
#### Understanding the `etc` Method
@@ -484,29 +678,50 @@ The `whereType` and `whereAllType` methods recognize the following types: `strin
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:
- image('avatar.jpg');
- namespace Tests\Feature;
+ $response = $this->post('/avatar', [
+ 'avatar' => $file,
+ ]);
- use Illuminate\Http\UploadedFile;
- use Illuminate\Support\Facades\Storage;
- use Tests\TestCase;
+ Storage::disk('avatars')->assertExists($file->hashName());
+});
+```
- class ExampleTest extends TestCase
+```php tab=PHPUnit
+image('avatar.jpg');
+ $file = UploadedFile::fake()->image('avatar.jpg');
- $response = $this->post('/avatar', [
- 'avatar' => $file,
- ]);
+ $response = $this->post('/avatar', [
+ 'avatar' => $file,
+ ]);
- Storage::disk('avatars')->assertExists($file->hashName());
- }
+ 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:
@@ -538,21 +753,33 @@ If needed, you may pass a `$mimeType` argument to the method to explicitly defin
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:
- view('welcome', ['name' => 'Taylor']);
- use Tests\TestCase;
+ $view->assertSee('Taylor');
+});
+```
- class ExampleTest extends TestCase
+```php tab=PHPUnit
+view('welcome', ['name' => 'Taylor']);
+ $view = $this->view('welcome', ['name' => 'Taylor']);
- $view->assertSee('Taylor');
- }
+ $view->assertSee('Taylor');
}
+}
+```
The `TestView` class provides the following assertion methods: `assertSee`, `assertSeeInOrder`, `assertSeeText`, `assertSeeTextInOrder`, `assertDontSee`, and `assertDontSeeText`.
@@ -1327,7 +1554,13 @@ Passing a closure as the second argument to the `assertViewHas` method will allo
In addition, view data may be accessed as array variables on the response, allowing you to conveniently inspect it:
- $this->assertEquals('Taylor', $response['name']);
+```php tab=Pest
+expect($response['name'])->toBe('Taylor');
+```
+
+```php tab=PHPUnit
+$this->assertEquals('Taylor', $response['name']);
+```
#### assertViewHasAll
diff --git a/mail.md b/mail.md
index 9ee73b47a6c..e0304c55c08 100644
--- a/mail.md
+++ b/mail.md
@@ -986,37 +986,72 @@ Laravel provides a variety of methods for inspecting your mailable's structure.
As you might expect, the "HTML" assertions assert that the HTML version of your mailable contains a given string, while the "text" assertions assert that the plain-text version of your mailable contains a given string:
- use App\Mail\InvoicePaid;
- use App\Models\User;
+```php tab=Pest
+use App\Mail\InvoicePaid;
+use App\Models\User;
+
+test('mailable content', function () {
+ $user = User::factory()->create();
+
+ $mailable = new InvoicePaid($user);
+
+ $mailable->assertFrom('jeffrey@example.com');
+ $mailable->assertTo('taylor@example.com');
+ $mailable->assertHasCc('abigail@example.com');
+ $mailable->assertHasBcc('victoria@example.com');
+ $mailable->assertHasReplyTo('tyler@example.com');
+ $mailable->assertHasSubject('Invoice Paid');
+ $mailable->assertHasTag('example-tag');
+ $mailable->assertHasMetadata('key', 'value');
+
+ $mailable->assertSeeInHtml($user->email);
+ $mailable->assertSeeInHtml('Invoice Paid');
+ $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
+
+ $mailable->assertSeeInText($user->email);
+ $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
+
+ $mailable->assertHasAttachment('/path/to/file');
+ $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
+ $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
+ $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
+ $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
+});
+```
- public function test_mailable_content(): void
- {
- $user = User::factory()->create();
-
- $mailable = new InvoicePaid($user);
-
- $mailable->assertFrom('jeffrey@example.com');
- $mailable->assertTo('taylor@example.com');
- $mailable->assertHasCc('abigail@example.com');
- $mailable->assertHasBcc('victoria@example.com');
- $mailable->assertHasReplyTo('tyler@example.com');
- $mailable->assertHasSubject('Invoice Paid');
- $mailable->assertHasTag('example-tag');
- $mailable->assertHasMetadata('key', 'value');
-
- $mailable->assertSeeInHtml($user->email);
- $mailable->assertSeeInHtml('Invoice Paid');
- $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
-
- $mailable->assertSeeInText($user->email);
- $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
-
- $mailable->assertHasAttachment('/path/to/file');
- $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
- $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
- $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
- $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
- }
+```php tab=PHPUnit
+use App\Mail\InvoicePaid;
+use App\Models\User;
+
+public function test_mailable_content(): void
+{
+ $user = User::factory()->create();
+
+ $mailable = new InvoicePaid($user);
+
+ $mailable->assertFrom('jeffrey@example.com');
+ $mailable->assertTo('taylor@example.com');
+ $mailable->assertHasCc('abigail@example.com');
+ $mailable->assertHasBcc('victoria@example.com');
+ $mailable->assertHasReplyTo('tyler@example.com');
+ $mailable->assertHasSubject('Invoice Paid');
+ $mailable->assertHasTag('example-tag');
+ $mailable->assertHasMetadata('key', 'value');
+
+ $mailable->assertSeeInHtml($user->email);
+ $mailable->assertSeeInHtml('Invoice Paid');
+ $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
+
+ $mailable->assertSeeInText($user->email);
+ $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
+
+ $mailable->assertHasAttachment('/path/to/file');
+ $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
+ $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
+ $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
+ $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
+}
+```
### Testing Mailable Sending
@@ -1025,38 +1060,68 @@ We suggest testing the content of your mailables separately from your tests that
You may use the `Mail` facade's `fake` method to prevent mail from being sent. After calling the `Mail` facade's `fake` method, you may then assert that mailables were instructed to be sent to users and even inspect the data the mailables received:
- instance(
- Service::class,
- Mockery::mock(Service::class, function (MockInterface $mock) {
- $mock->shouldReceive('process')->once();
- })
- );
- }
+```php tab=Pest
+use App\Service;
+use Mockery;
+use Mockery\MockInterface;
+
+test('something can be mocked', function () {
+ $this->instance(
+ Service::class,
+ Mockery::mock(Service::class, function (MockInterface $mock) {
+ $mock->shouldReceive('process')->once();
+ })
+ );
+});
+```
+
+```php tab=PHPUnit
+use App\Service;
+use Mockery;
+use Mockery\MockInterface;
+
+public function test_something_can_be_mocked(): void
+{
+ $this->instance(
+ Service::class,
+ Mockery::mock(Service::class, function (MockInterface $mock) {
+ $mock->shouldReceive('process')->once();
+ })
+ );
+}
+```
In order to make this more convenient, you may use the `mock` method that is provided by Laravel's base test case class. For example, the following example is equivalent to the example above:
@@ -88,27 +105,46 @@ Unlike traditional static method calls, [facades](/docs/{{version}}/facades) (in
We can mock the call to the `Cache` facade by using the `shouldReceive` method, which will return an instance of a [Mockery](https://github.com/padraic/mockery) mock. Since facades are actually resolved and managed by the Laravel [service container](/docs/{{version}}/container), they have much more testability than a typical static class. For example, let's mock our call to the `Cache` facade's `get` method:
- once()
+ ->with('key')
+ ->andReturn('value');
+
+ $response = $this->get('/users');
+
+ // ...
+});
+```
- class UserControllerTest extends TestCase
+```php tab=PHPUnit
+once()
- ->with('key')
- ->andReturn('value');
+ Cache::shouldReceive('get')
+ ->once()
+ ->with('key')
+ ->andReturn('value');
- $response = $this->get('/users');
+ $response = $this->get('/users');
- // ...
- }
+ // ...
}
+}
+```
> [!WARNING]
> You should not mock the `Request` facade. Instead, pass the input you desire into the [HTTP testing methods](/docs/{{version}}/http-tests) such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, call the `Config::set` method in your tests.
@@ -118,46 +154,86 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method,
If you would like to [spy](http://docs.mockery.io/en/latest/reference/spies.html) on a facade, you may call the `spy` method on the corresponding facade. Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed:
- use Illuminate\Support\Facades\Cache;
+```php tab=Pest
+get('/');
+test('values are be stored in cache', function () {
+ Cache::spy();
- $response->assertStatus(200);
+ $response = $this->get('/');
- Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
- }
+ $response->assertStatus(200);
+
+ Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
+});
+```
+
+```php tab=PHPUnit
+use Illuminate\Support\Facades\Cache;
+
+public function test_values_are_be_stored_in_cache(): void
+{
+ Cache::spy();
+
+ $response = $this->get('/');
+
+ $response->assertStatus(200);
+
+ Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
+}
+```
## Interacting With Time
When testing, you may occasionally need to modify the time returned by helpers such as `now` or `Illuminate\Support\Carbon::now()`. Thankfully, Laravel's base feature test class includes helpers that allow you to manipulate the current time:
- use Illuminate\Support\Carbon;
-
- public function test_time_can_be_manipulated(): void
- {
- // Travel into the future...
- $this->travel(5)->milliseconds();
- $this->travel(5)->seconds();
- $this->travel(5)->minutes();
- $this->travel(5)->hours();
- $this->travel(5)->days();
- $this->travel(5)->weeks();
- $this->travel(5)->years();
-
- // Travel into the past...
- $this->travel(-5)->hours();
-
- // Travel to an explicit time...
- $this->travelTo(now()->subHours(6));
-
- // Return back to the present time...
- $this->travelBack();
- }
+```php tab=Pest
+test('time can be manipulated', function () {
+ // Travel into the future...
+ $this->travel(5)->milliseconds();
+ $this->travel(5)->seconds();
+ $this->travel(5)->minutes();
+ $this->travel(5)->hours();
+ $this->travel(5)->days();
+ $this->travel(5)->weeks();
+ $this->travel(5)->years();
+
+ // Travel into the past...
+ $this->travel(-5)->hours();
+
+ // Travel to an explicit time...
+ $this->travelTo(now()->subHours(6));
+
+ // Return back to the present time...
+ $this->travelBack();
+});
+```
+
+```php tab=PHPUnit
+public function test_time_can_be_manipulated(): void
+{
+ // Travel into the future...
+ $this->travel(5)->milliseconds();
+ $this->travel(5)->seconds();
+ $this->travel(5)->minutes();
+ $this->travel(5)->hours();
+ $this->travel(5)->days();
+ $this->travel(5)->weeks();
+ $this->travel(5)->years();
+
+ // Travel into the past...
+ $this->travel(-5)->hours();
+
+ // Travel to an explicit time...
+ $this->travelTo(now()->subHours(6));
+
+ // Return back to the present time...
+ $this->travelBack();
+}
+```
You may also provide a closure to the various time travel methods. The closure will be invoked with time frozen at the specified time. Once the closure has executed, time will resume as normal:
@@ -185,13 +261,27 @@ The `freezeTime` method may be used to freeze the current time. Similarly, the `
As you would expect, all of the methods discussed above are primarily useful for testing time sensitive application behavior, such as locking inactive posts on a discussion forum:
- use App\Models\Thread;
-
- public function test_forum_threads_lock_after_one_week_of_inactivity()
- {
- $thread = Thread::factory()->create();
-
- $this->travel(1)->week();
-
- $this->assertTrue($thread->isLockedByInactivity());
- }
+```php tab=Pest
+use App\Models\Thread;
+
+test('forum threads lock after one week of inactivity', function () {
+ $thread = Thread::factory()->create();
+
+ $this->travel(1)->week();
+
+ expect($thread->isLockedByInactivity())->toBeTrue();
+});
+```
+
+```php tab=PHPUnit
+use App\Models\Thread;
+
+public function test_forum_threads_lock_after_one_week_of_inactivity()
+{
+ $thread = Thread::factory()->create();
+
+ $this->travel(1)->week();
+
+ $this->assertTrue($thread->isLockedByInactivity());
+}
+```
diff --git a/notifications.md b/notifications.md
index 403b136721e..39004ee7413 100644
--- a/notifications.md
+++ b/notifications.md
@@ -1347,39 +1347,70 @@ You may use the `Notification` facade's `fake` method to prevent notifications f
After calling the `Notification` facade's `fake` method, you may then assert that notifications were instructed to be sent to users and even inspect the data the notifications received:
- create(),
- ['create-servers']
- );
+test('servers can be created', function () {
+ Passport::actingAs(
+ User::factory()->create(),
+ ['create-servers']
+ );
- $response = $this->post('/api/create-server');
+ $response = $this->post('/api/create-server');
- $response->assertStatus(201);
- }
+ $response->assertStatus(201);
+});
+```
+
+```php tab=PHPUnit
+use App\Models\User;
+use Laravel\Passport\Passport;
+
+public function test_servers_can_be_created(): void
+{
+ Passport::actingAs(
+ User::factory()->create(),
+ ['create-servers']
+ );
+
+ $response = $this->post('/api/create-server');
+
+ $response->assertStatus(201);
+}
+```
Passport's `actingAsClient` method may be used to specify the currently authenticated client as well as its scopes. The first argument given to the `actingAsClient` method is the client instance and the second is an array of scopes that should be granted to the client's token:
- use Laravel\Passport\Client;
- use Laravel\Passport\Passport;
+```php tab=Pest
+use Laravel\Passport\Client;
+use Laravel\Passport\Passport;
- public function test_orders_can_be_retrieved(): void
- {
- Passport::actingAsClient(
- Client::factory()->create(),
- ['check-status']
- );
+test('orders can be retrieved', function () {
+ Passport::actingAsClient(
+ Client::factory()->create(),
+ ['check-status']
+ );
- $response = $this->get('/api/orders');
+ $response = $this->get('/api/orders');
- $response->assertStatus(200);
- }
+ $response->assertStatus(200);
+});
+```
+
+```php tab=PHPUnit
+use Laravel\Passport\Client;
+use Laravel\Passport\Passport;
+
+public function test_orders_can_be_retrieved(): void
+{
+ Passport::actingAsClient(
+ Client::factory()->create(),
+ ['check-status']
+ );
+
+ $response = $this->get('/api/orders');
+
+ $response->assertStatus(200);
+}
+```
diff --git a/pennant.md b/pennant.md
index e0f73439176..e8a79140368 100644
--- a/pennant.md
+++ b/pennant.md
@@ -800,7 +800,17 @@ Feature::define('purchase-button', fn () => Arr::random([
To modify the feature's returned value in your tests, you may re-define the feature at the beginning of the test. The following test will always pass, even though the `Arr::random()` implementation is still present in the service provider:
-```php
+```php tab=Pest
+use Laravel\Pennant\Feature;
+
+test('it can control feature values', function () {
+ Feature::define('purchase-button', 'seafoam-green');
+
+ $this->assertSame('seafoam-green', Feature::value('purchase-button'));
+});
+```
+
+```php tab=PHPUnit
use Laravel\Pennant\Feature;
public function test_it_can_control_feature_values()
diff --git a/processes.md b/processes.md
index fba80d999e0..15666a6d86d 100644
--- a/processes.md
+++ b/processes.md
@@ -394,7 +394,30 @@ Route::get('/import', function () {
When testing this route, we can instruct Laravel to return a fake, successful process result for every invoked process by calling the `fake` method on the `Process` facade with no arguments. In addition, we can even [assert](#available-assertions) that a given process was "run":
-```php
+```php tab=Pest
+get('/import');
+
+ // Simple process assertion...
+ Process::assertRan('bash import.sh');
+
+ // Or, inspecting the process configuration...
+ Process::assertRan(function (PendingProcess $process, ProcessResult $result) {
+ return $process->command === 'bash import.sh' &&
+ $process->timeout === 60;
+ });
+});
+```
+
+```php tab=PHPUnit
## Running Tests
-Laravel provides amazing testing support out of the box, and you may use Sail's `test` command to run your applications [feature and unit tests](/docs/{{version}}/testing). Any CLI options that are accepted by PHPUnit may also be passed to the `test` command:
+Laravel provides amazing testing support out of the box, and you may use Sail's `test` command to run your applications [feature and unit tests](/docs/{{version}}/testing). Any CLI options that are accepted by Pest / PHPUnit may also be passed to the `test` command:
```shell
sail test
diff --git a/sanctum.md b/sanctum.md
index ca97137aa9a..acac48dedd2 100644
--- a/sanctum.md
+++ b/sanctum.md
@@ -440,20 +440,38 @@ To allow users to revoke API tokens issued to mobile devices, you may list them
While testing, the `Sanctum::actingAs` method may be used to authenticate a user and specify which abilities should be granted to their token:
- use App\Models\User;
- use Laravel\Sanctum\Sanctum;
+```php tab=Pest
+use App\Models\User;
+use Laravel\Sanctum\Sanctum;
- public function test_task_list_can_be_retrieved(): void
- {
- Sanctum::actingAs(
- User::factory()->create(),
- ['view-tasks']
- );
+test('task list can be retrieved', function () {
+ Sanctum::actingAs(
+ User::factory()->create(),
+ ['view-tasks']
+ );
- $response = $this->get('/api/task');
+ $response = $this->get('/api/task');
- $response->assertOk();
- }
+ $response->assertOk();
+});
+```
+
+```php tab=PHPUnit
+use App\Models\User;
+use Laravel\Sanctum\Sanctum;
+
+public function test_task_list_can_be_retrieved(): void
+{
+ Sanctum::actingAs(
+ User::factory()->create(),
+ ['view-tasks']
+ );
+
+ $response = $this->get('/api/task');
+
+ $response->assertOk();
+}
+```
If you would like to grant all abilities to the token, you should include `*` in the ability list provided to the `actingAs` method:
diff --git a/structure.md b/structure.md
index 1598f50f2d4..f9152bf9562 100644
--- a/structure.md
+++ b/structure.md
@@ -91,7 +91,7 @@ The `storage/app/public` directory may be used to store user-generated files, su
#### The Tests Directory
-The `tests` directory contains your automated tests. Example [PHPUnit](https://phpunit.de/) unit tests and feature tests are provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands. Or, if you would like a more detailed and beautiful representation of your test results, you may run your tests using the `php artisan test` Artisan command.
+The `tests` directory contains your automated tests. Example [Pest](https://pestphp.com) or [PHPUnit](https://phpunit.de/) unit tests and feature tests are provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands. Or, if you would like a more detailed and beautiful representation of your test results, you may run your tests using the `php artisan test` Artisan command.
#### The Vendor Directory
diff --git a/testing.md b/testing.md
index 69ac8ac9284..60bd9e03a38 100644
--- a/testing.md
+++ b/testing.md
@@ -11,13 +11,13 @@
## Introduction
-Laravel is built with testing in mind. In fact, support for testing with PHPUnit is included out of the box and a `phpunit.xml` file is already set up for your application. The framework also ships with convenient helper methods that allow you to expressively test your applications.
+Laravel is built with testing in mind. In fact, support for testing with [Pest](https://pestphp.com) and [PHPUnit](https://phpunit.de) is included out of the box and a `phpunit.xml` file is already set up for your application. The framework also ships with convenient helper methods that allow you to expressively test your applications.
By default, your application's `tests` directory contains two directories: `Feature` and `Unit`. Unit tests are tests that focus on a very small, isolated portion of your code. In fact, most unit tests probably focus on a single method. Tests within your "Unit" test directory do not boot your Laravel application and therefore are unable to access your application's database or other framework services.
Feature tests may test a larger portion of your code, including how several objects interact with each other or even a full HTTP request to a JSON endpoint. **Generally, most of your tests should be feature tests. These types of tests provide the most confidence that your system as a whole is functioning as intended.**
-An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, execute the `vendor/bin/phpunit` or `php artisan test` commands to run your tests.
+An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, execute the `vendor/bin/pest`, `vendor/bin/phpunit`, or `php artisan test` commands to run your tests.
## Environment
@@ -29,7 +29,7 @@ You are free to define other testing environment configuration values as necessa
#### The `.env.testing` Environment File
-In addition, you may create a `.env.testing` file in the root of your project. This file will be used instead of the `.env` file when running PHPUnit tests or executing Artisan commands with the `--env=testing` option.
+In addition, you may create a `.env.testing` file in the root of your project. This file will be used instead of the `.env` file when running Pest and PHPUnit tests or executing Artisan commands with the `--env=testing` option.
#### The `CreatesApplication` Trait
@@ -58,37 +58,51 @@ php artisan make:test UserTest --pest
php artisan make:test UserTest --unit --pest
```
-> [!NOTE]
+> [!NOTE]
> Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization).
-Once the test has been generated, you may define test methods as you normally would using [PHPUnit](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal:
+Once the test has been generated, you may define test as you normally would using Pest or PHPUnit. To run your tests, execute the `vendor/bin/pest`, `vendor/bin/phpunit`, or `php artisan test` command from your terminal:
- toBeTrue();
+});
+```
- namespace Tests\Unit;
+```php tab=PHPUnit
+assertTrue(true);
- }
+ $this->assertTrue(true);
}
+}
+```
-> [!WARNING]
+> [!WARNING]
> If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. Typically, you should invoke `parent::setUp()` at the start of your own `setUp` method, and `parent::tearDown()` at the end of your `tearDown` method.
## Running Tests
-As mentioned previously, once you've written tests, you may run them using `phpunit`:
+As mentioned previously, once you've written tests, you may run them using `pest` or `phpunit`:
-```shell
+```shell tab=Pest
+./vendor/bin/pest
+```
+
+```shell tab=PHPUnit
./vendor/bin/phpunit
```
@@ -107,7 +121,7 @@ php artisan test --testsuite=Feature --stop-on-failure
### Running Tests in Parallel
-By default, Laravel and PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, you should install the `brianium/paratest` Composer package as a "dev" dependency. Then, include the `--parallel` option when executing the `test` Artisan command:
+By default, Laravel and Pest / PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, you should install the `brianium/paratest` Composer package as a "dev" dependency. Then, include the `--parallel` option when executing the `test` Artisan command:
```shell
composer require brianium/paratest --dev
@@ -122,7 +136,7 @@ php artisan test --parallel --processes=4
```
> [!WARNING]
-> When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available.
+> When running tests in parallel, some Pest / PHPUnit options (such as `--do-not-cache-result`) may not be available.
#### Parallel Testing and Databases
diff --git a/vite.md b/vite.md
index 3acd2bb8f27..021e9d2240d 100644
--- a/vite.md
+++ b/vite.md
@@ -602,7 +602,15 @@ Laravel's Vite integration will attempt to resolve your assets while running you
If you would prefer to mock Vite during testing, you may call the `withoutVite` method, which is available for any tests that extend Laravel's `TestCase` class:
-```php
+```php tab=Pest
+test('without vite example', function () {
+ $this->withoutVite();
+
+ // ...
+});
+```
+
+```php tab=PHPUnit
use Tests\TestCase;
class ExampleTest extends TestCase