> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Write automated tests for your Laravel application using Pest or PHPUnit.

## Why write tests?

Tests verify that your code behaves as expected and alert you when changes break existing functionality. In team projects, a good test suite lets everyone refactor and ship with confidence.

Laravel has first-class support for testing built in. Every new project ships with a `phpunit.xml` configuration file and a `tests/` directory, and supports both [Pest](https://pestphp.com) and [PHPUnit](https://phpunit.de/).

<Tip>
  Pest is built on top of PHPUnit and offers a more concise, readable syntax. If you're just getting started with testing, Pest is the recommended choice.
</Tip>

## The `tests/` directory

```
tests/
├── Feature/      # Feature tests
│   └── ExampleTest.php
├── Unit/         # Unit tests
│   └── ExampleTest.php
└── TestCase.php
```

* **`Feature/`** — Tests that cover larger units of functionality, including HTTP requests. Most of your tests will live here.
* **`Unit/`** — Tests for individual classes or methods in isolation. The Laravel application is not booted, so these tests cannot use the database or other framework features.

## Creating tests

Use the `make:test` Artisan command to generate a new test file:

```shell theme={null}
# Create a feature test (default)
php artisan make:test PostTest

# Create a unit test
php artisan make:test PostTest --unit
```

## Running tests

Run all tests with:

```shell theme={null}
php artisan test
```

You can also run `vendor/bin/pest` or `vendor/bin/phpunit` directly, but `php artisan test` produces more readable output.

Useful options:

```shell theme={null}
# Run only feature tests
php artisan test --testsuite=Feature

# Stop on the first failure
php artisan test --stop-on-failure
```

## Writing tests

### Basic assertions

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('true is true', function () {
      expect(true)->toBeTrue();
  });

  test('string contains expected text', function () {
      $message = 'Hello, Laravel!';

      expect($message)->toContain('Laravel');
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Unit;

  use PHPUnit\Framework\TestCase;

  class ExampleTest extends TestCase
  {
      public function test_true_is_true(): void
      {
          $this->assertTrue(true);
      }

      public function test_string_contains_laravel(): void
      {
          $message = 'Hello, Laravel!';

          $this->assertStringContainsString('Laravel', $message);
      }
  }
  ```
</CodeGroup>

### Common assertions

| Pest | PHPUnit | Description |
| - | - | - |
| `expect($x)->toBe($y)` | `$this->assertSame($y, $x)` | Strictly equal |
| `expect($x)->toEqual($y)` | `$this->assertEquals($y, $x)` | Equal (type-coerced) |
| `expect($x)->toBeTrue()` | `$this->assertTrue($x)` | Is `true` |
| `expect($x)->toBeFalse()` | `$this->assertFalse($x)` | Is `false` |
| `expect($x)->toBeNull()` | `$this->assertNull($x)` | Is `null` |
| `expect($x)->toContain($y)` | `$this->assertStringContainsString($y, $x)` | String contains value |
| `expect($x)->toHaveCount($n)` | `$this->assertCount($n, $x)` | Collection has N items |

## HTTP tests

Laravel's HTTP testing tools let you simulate requests to your application without running a real HTTP server. This is done inside `Feature/` tests.

### Testing a page response

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('home page returns 200', function () {
      $response = $this->get('/');

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

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Tests\TestCase;

  class ExampleTest extends TestCase
  {
      public function test_home_page_returns_200(): void
      {
          $response = $this->get('/');

          $response->assertStatus(200);
      }
  }
  ```
</CodeGroup>

### Testing CRUD operations

Using a `Post` model as an example:

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\Post;
  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('post list page is accessible', function () {
      Post::factory()->count(3)->create();

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

      $response->assertStatus(200);
      $response->assertSee('Posts');
  });

  test('a post can be created', function () {
      $response = $this->post('/posts', [
          'title' => 'My first post',
          'body'  => 'Hello, world!',
      ]);

      $response->assertRedirect('/posts');
      $this->assertDatabaseHas('posts', ['title' => 'My first post']);
  });

  test('a post can be deleted', function () {
      $post = Post::factory()->create();

      $response = $this->delete("/posts/{$post->id}");

      $response->assertRedirect('/posts');
      $this->assertDatabaseMissing('posts', ['id' => $post->id]);
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use App\Models\Post;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class PostTest extends TestCase
  {
      use RefreshDatabase;

      public function test_post_list_page_is_accessible(): void
      {
          Post::factory()->count(3)->create();

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

          $response->assertStatus(200);
          $response->assertSee('Posts');
      }

      public function test_a_post_can_be_created(): void
      {
          $response = $this->post('/posts', [
              'title' => 'My first post',
              'body'  => 'Hello, world!',
          ]);

          $response->assertRedirect('/posts');
          $this->assertDatabaseHas('posts', ['title' => 'My first post']);
      }

      public function test_a_post_can_be_deleted(): void
      {
          $post = Post::factory()->create();

          $response = $this->delete("/posts/{$post->id}");

          $response->assertRedirect('/posts');
          $this->assertDatabaseMissing('posts', ['id' => $post->id]);
      }
  }
  ```
</CodeGroup>

### Useful response assertions

| Method | Description |
| - | - |
| `assertStatus(200)` | Assert the response has the given status code |
| `assertOk()` | Assert status code is 200 |
| `assertRedirect('/path')` | Assert a redirect to the given URL |
| `assertSee('text')` | Assert the response body contains the given text |
| `assertDontSee('text')` | Assert the response body does not contain the text |
| `assertJson([...])` | Assert the JSON response contains the given data |
| `assertDatabaseHas('table', [...])` | Assert a record exists in the database |
| `assertDatabaseMissing('table', [...])` | Assert a record does not exist in the database |

<Info>
  The `RefreshDatabase` trait resets the database after each test, preventing data from one test leaking into another.
</Info>

## Testing authenticated routes

Use `actingAs()` to authenticate a user for a test:

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('authenticated users can view the dashboard', function () {
      $user = User::factory()->create();

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

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

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class DashboardTest extends TestCase
  {
      use RefreshDatabase;

      public function test_authenticated_users_can_view_the_dashboard(): void
      {
          $user = User::factory()->create();

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

          $response->assertStatus(200);
      }
  }
  ```
</CodeGroup>

## Test environment configuration

### `phpunit.xml`

The `phpunit.xml` file at the project root configures the test environment. By default, the session and cache use the `array` driver so data is not persisted between requests:

```xml theme={null}
<env name="APP_ENV" value="testing"/>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
```

For database tests, use an in-memory SQLite database to keep tests fast and self-contained:

```xml theme={null}
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
```

### `.env.testing`

Create a `.env.testing` file at the project root to override environment variables specifically for the test environment. Laravel loads this file instead of `.env` when running tests:

```ini theme={null}
APP_ENV=testing
DB_CONNECTION=sqlite
DB_DATABASE=:memory:
```

<Warning>
  If you have cached your configuration, run `php artisan config:clear` before running tests. Stale cache can cause your test environment variables to be ignored.
</Warning>

## Running tests in parallel

As your test suite grows, execution time increases. The `--parallel` flag runs tests across multiple processes simultaneously. First install the `brianium/paratest` package, then use the flag:

```shell theme={null}
composer require brianium/paratest --dev
```

```shell theme={null}
php artisan test --parallel
```

By default, Laravel creates one process per CPU core. Control this with `--processes`:

```shell theme={null}
php artisan test --parallel --processes=4
```

<Tip>
  Parallel tests require each process to have its own isolated database. Using `RefreshDatabase` with in-memory SQLite (configured in `phpunit.xml`) satisfies this requirement automatically.
</Tip>

## Next steps

<Card title="HTTP testing" icon="arrow-right" href="/en/http-tests">
  Explore advanced HTTP testing — authenticated requests, JSON assertions, file uploads, and more.
</Card>


## Related topics

- [Testing](/en/packages/laravel-bluesky/testing.md)
- [Database Testing](/en/database-testing.md)
- [Advanced Testing with Pest](/en/advanced/testing-pest.md)
- [Testing Laravel packages with Orchestra Testbench](/en/advanced/package-testing.md)
- [Getting started with Laravel testing using Pest PHP](/en/blog/pest-introduction.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.