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

# Introducción a las pruebas

> Cómo escribir pruebas para aplicaciones Laravel con Pest y PHPUnit

## ¿Qué son las pruebas?

Las pruebas son un mecanismo automatizado para verificar que el código funciona como se espera.
Al escribirlas puedes comprobar rápidamente si los cambios rompen algo del comportamiento existente.
En un equipo, tener pruebas da confianza para modificar y revisar el código.

Laravel incluye soporte de pruebas de fábrica y admite tanto [Pest](https://pestphp.com) como [PHPUnit](https://phpunit.de).
Al instalar el proyecto ya vienen preparados el archivo de configuración `phpunit.xml` y el directorio `tests/`.

<Tip>
  Pest se construye sobre PHPUnit y permite escribir pruebas con una sintaxis más concisa y legible. Si empiezas ahora, es recomendable elegir Pest.
</Tip>

## Estructura del directorio `tests/`

```
tests/
├── Feature/      # Pruebas de funcionalidad
│   └── ExampleTest.php
├── Unit/         # Pruebas unitarias
│   └── ExampleTest.php
└── TestCase.php
```

* **`Feature/`** — Pruebas de funcionalidad. Sirven para pruebas de mayor tamaño que incluyen solicitudes HTTP. Cubren la integración entre varios objetos o endpoints de API. **La mayoría de tus pruebas irán aquí.**
* **`Unit/`** — Pruebas unitarias. Sirven para pruebas pequeñas de una clase o método. La aplicación Laravel no se arranca, así que no puedes usar la base de datos ni otras funcionalidades del framework.

## Crear una prueba

Crea una nueva clase de prueba con el comando Artisan `make:test`.

```shell theme={null}
# Crea una prueba Feature (por defecto)
php artisan make:test TodoTest

# Crea una prueba Unit
php artisan make:test TodoTest --unit
```

Se genera `tests/Feature/TodoTest.php`.

## Ejecutar las pruebas

Ejecuta las pruebas con `php artisan test`.

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

También puedes ejecutar `vendor/bin/pest` o `vendor/bin/phpunit` directamente, pero `php artisan test` ofrece un formato de salida más legible.

Para ejecutar una suite concreta usa opciones.

```shell theme={null}
# Solo pruebas Feature
php artisan test --testsuite=Feature

# Para al primer fallo
php artisan test --stop-on-failure
```

## Prueba básica

Ejemplo simple de aserciones.

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

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

  test('la cadena contiene el texto esperado', 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>

### Aserciones habituales

| Pest                          | PHPUnit                                     | Descripción                    |
| ----------------------------- | ------------------------------------------- | ------------------------------ |
| `expect($x)->toBe($y)`        | `$this->assertSame($y, $x)`                 | Estrictamente iguales          |
| `expect($x)->toEqual($y)`     | `$this->assertEquals($y, $x)`               | Iguales (sin importar el tipo) |
| `expect($x)->toBeTrue()`      | `$this->assertTrue($x)`                     | Es `true`                      |
| `expect($x)->toBeFalse()`     | `$this->assertFalse($x)`                    | Es `false`                     |
| `expect($x)->toBeNull()`      | `$this->assertNull($x)`                     | Es `null`                      |
| `expect($x)->toContain($y)`   | `$this->assertStringContainsString($y, $x)` | Contiene el string             |
| `expect($x)->toHaveCount($n)` | `$this->assertCount($n, $x)`                | Número de elementos coincide   |

## Pruebas HTTP

Las pruebas HTTP de Laravel simulan solicitudes a rutas sin necesidad de arrancar un servidor real.
Es una funcionalidad potente disponible en las pruebas de `Feature/`.

### Comprobar que una página se muestra

Con `get()` envías una solicitud GET y verificas la respuesta.

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

  test('se muestra la portada', 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_top_page_is_displayed(): void
      {
          $response = $this->get('/');

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

### Ejemplo de pruebas HTTP de una app ToDo

Con un modelo `Todo`, probamos cada operación CRUD.

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

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

  uses(RefreshDatabase::class);

  test('se muestra la lista de ToDos', function () {
      Todo::factory()->count(3)->create();

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

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

  test('se puede crear un ToDo', function () {
      $response = $this->post('/todos', [
          'title' => 'Aprender Laravel',
      ]);

      $response->assertRedirect('/todos');
      $this->assertDatabaseHas('todos', ['title' => 'Aprender Laravel']);
  });

  test('se puede eliminar un ToDo', function () {
      $todo = Todo::factory()->create();

      $response = $this->delete("/todos/{$todo->id}");

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

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

  namespace Tests\Feature;

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

  class TodoTest extends TestCase
  {
      use RefreshDatabase;

      public function test_todo_list_is_displayed(): void
      {
          Todo::factory()->count(3)->create();

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

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

      public function test_todo_can_be_created(): void
      {
          $response = $this->post('/todos', [
              'title' => 'Aprender Laravel',
          ]);

          $response->assertRedirect('/todos');
          $this->assertDatabaseHas('todos', ['title' => 'Aprender Laravel']);
      }

      public function test_todo_can_be_deleted(): void
      {
          $todo = Todo::factory()->create();

          $response = $this->delete("/todos/{$todo->id}");

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

### Aserciones habituales de respuesta

| Método                                  | Descripción                                 |
| --------------------------------------- | ------------------------------------------- |
| `assertStatus(200)`                     | Verifica el código de estado HTTP           |
| `assertOk()`                            | Verifica que el código es 200               |
| `assertRedirect('/path')`               | Verifica la redirección a la URL indicada   |
| `assertSee('texto')`                    | Verifica que el cuerpo contiene el texto    |
| `assertDontSee('texto')`                | Verifica que el cuerpo no contiene el texto |
| `assertJson([...])`                     | Verifica datos en una respuesta JSON        |
| `assertDatabaseHas('table', [...])`     | Verifica que un registro existe en la BD    |
| `assertDatabaseMissing('table', [...])` | Verifica que un registro no existe en la BD |

<Info>
  El trait `RefreshDatabase` reinicia la base de datos tras cada prueba. Sirve para evitar interferencias entre pruebas.
</Info>

## Entorno de pruebas

### Configuración de `phpunit.xml`

Configura el entorno de pruebas en `phpunit.xml` en la raíz.
Por defecto se usan drivers `array` para sesión y caché, evitando que queden datos entre pruebas.

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

Para la base de datos es habitual usar SQLite en memoria, que no deja archivos.

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

### Archivo `.env.testing`

Si creas `.env.testing` en la raíz, se lee en lugar de `.env` al ejecutar pruebas.
Es útil para separar la configuración de conexión o de servicios externos para pruebas.

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

<Warning>
  Si tienes caché de configuración, ejecuta `php artisan config:clear` antes de las pruebas. De lo contrario podría usarse una caché antigua.
</Warning>

## Ejecución en paralelo

Cuando hay muchas pruebas, el tiempo se alarga.
Con `--parallel` puedes ejecutar en varios procesos a la vez y reducir el tiempo.

Primero instala el paquete `brianium/paratest`.

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

Después ejecuta con la opción `--parallel`.

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

Por defecto usa tantos procesos como núcleos de CPU. Indica el número con `--processes`.

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

<Tip>
  Con pruebas en paralelo, cada proceso debe usar una base de datos independiente. `RefreshDatabase` combinado con SQLite en memoria de `phpunit.xml` funciona sin problemas.
</Tip>

## Próximos pasos

<Card title="Pruebas HTTP" icon="arrow-right" href="/es/http-tests">
  Aprende con más detalle a simular solicitudes, hacer pruebas con autenticación y aserciones JSON.
</Card>


## Related topics

- [Introducción a las relaciones de Eloquent](/es/eloquent-relationships.md)
- [Pruebas HTTP](/es/http-tests.md)
- [Pruebas de base de datos](/es/database-testing.md)
- [Introducción a la autenticación](/es/authentication.md)
- [Estructura de directorios](/es/directory-structure.md)
