Demystifying tests in Laravel
Tags: web, php, laravel
Published: 2022-12-10 15:39:42

---
title: Demystifying tests in Laravel
published: true
description: A succint post to show you how easy it is to test a Laravel app.
tags: web, php, laravel
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9c9ne86xwkawvwklyfc.png
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-11 17:14 +0000
---

Hi and welcome for another article 👋

This one will be a rapid-fire oriented post so that you can both see how easy it is to test your Laravel app, and you can refer to it whenever you want to remember how to test something.

Everytime you want to create a test, and the test class do not exist yet, just run this command.

```bash
php artisan make:test LoginTest
```

And you run your tests using this command.

```bash
php artisan test
```

Without further do, let's get into it!

- [Folder architecture suggestion](#folder-architecture-suggestion)
- [1. Assert a route returns response without errors](#1- assert-a-route-returns-response-without-errors)
- [1. Assert a user is logged](#1-assert-a-user-is-logged)
- [2. Assert an authenticated user can perform actions](#2- assert-an-authenticated-user-can-perform-actions)
- [3. Assert a model has been saved after form submission](#3-assert-a-model-has-been-saved-after-form-submission)
- [4. Assert a file has been uploaded](#4-assert-a-file-has-been-uploaded)
- [5. Creating a fake model with relationships](#5-creating-a-fake-model-with-relationships)
- [6. Assert that a session flash message is visible](#6- assert-that-a-session-flash-message-is-visible)
- [7. Assert a validation error message is returned](#7- assert-a-validation-error-message-is-returned)
- [8. Assert an authorization](#8-assert-an-authorization)
- [9. Assert a command ended without errors](#9-assert-a-command-ended-without-errors)
- [11. Assert a json content is sent](#11-assert-a-json-content-is-sent)
- [Conclusion](#conclusion)

## Folder architecture suggestion

If you have a lot of tests, naming can be challenging. To not think of it, and if your test file is focused on a controller, model, ... you can just name the file the same as the entity you test.

```
your-app/
├── app/
│   ├── Http/
│   │   └── Controllers/
│   │       ├── LoginController.php
│   │       └── PostController.php
│   ├── Models/
│   │   ├── Post.php
│   │   └── User.php
│   ├── Rules/
│   │   └── BarCodeValid.php
│   └── Helpers/
│       └── Color.php
└── tests/
    ├── Feature/
    │   ├── Http/
    │   │   └── Controllers/
    │   │       ├── LoginControllerTest.php
    │   │       └── PostControllerTest.php
    │   ├── Models/
    │   │   ├── PostTest.php
    │   │   └── UserTest.php
    │   └── Rules/
    │       └── BarCodeValidTest.php
    └── Unit/
        └── Helpers/
            └── ColorTest.php
```

## 1. Assert a route returns response without errors

```php
namespace Tests\Feature;

use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class ContactUsControllerTest extends TestCase
{
  use WithFaker;

  public function testContactUsPageRendersWell()
  {
    $this
      ->get(route("contact-us.index"))
      ->assertOk();
  }
}
```

## 1. Assert a user is logged

```php
namespace Tests\Feature;

use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class LoginControllerTest extends TestCase
{
  use WithFaker;

  public function testUserIsLoggedAfterFormIsSubmitted()
  {
    $password = $this->faker->password();
    $user = User::factory()->create();

    $this->post(route("login"), [
        "email" => $user->email,
        "password" => $password,
      ])
      ->assertValid()
      ->assertRedirect(route("dashboard"));

    $this->assertAuthenticated();
    // or
    $this->assertAuthenticatedAs($user);
  }
}
```

## 2. Assert an authenticated user can perform actions

```php
namespace Tests\Feature;

use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class PostControllerTest extends TestCase
{
  use WithFaker;

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

    $this->actingAs($user)
      ->post(route("post.store"), [
        "title" => $this->faker->sentence(),
        "content" => $this->faker->text(),
      ])
      ->assertValid();
  }
}
```

## 3. Assert a model has been saved after form submission

```php
namespace Tests\Feature;

use App\Models\Item;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class ItemControllerTest extends TestCase
{
  use WithFaker;

  public function testItemSavedAfterUserSubmitsForm()
  {
    $user = User::factory()->create();
    $name = $this->faker->name();

    $this->assertDatabaseMissing(Item::class, [
      "name" => $name,
    ]);

    $this->actingAs($user)
      ->post(route("item.store"), [
        "name" => $name,
      ])
      ->assertValid();

    $this->assertDatabaseHas(Item::class, [
      "name" => $name,
    ]);
  }
}
```

## 4. Assert a file has been uploaded

```php
namespace Tests\Feature;

use App\Models\User;
use Illuminate\Http\UploadedFile;
use Tests\TestCase;

class SettingControllerTest extends TestCase
{
  public function testUserCanUploadItsProfilePicture()
  {
    $user = User::factory()->create();

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

    $this->assertDatabaseMissing(User::class, [
      "user_id" => $user->id,
      "profile_picture" => $file->hashName(),
    ]);

    $this->actingAs($user)
      ->post(route("setting.update"), [
        "profile_picture" => $file,
      ])
      ->assertValid();

    $this->assertDatabaseHas(User::class, [
      "user_id" => $user->id,
      "profile_picture" => $file->hashName(),
    ]);
  }
}
```

## 5. Creating a fake model with relationships

```php
namespace Tests\Feature;

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

class PostControllerTest extends TestCase
{
  public function testAuthorCanEditPost()
  {
    $user = User::factory()->create();

    // Creates a post with 5 comments, each comments has 25 likes
    $post = Post::factory()
      ->for($user, "author")
      ->has(
        Comment::factory()
          ->has(Like::factory()->count(25))
          ->count(5)
      )
      ->create();
  }
}
```

## 6. Assert that a session flash message is visible

```php
namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class PostControllerTest extends TestCase
{
  use WithFaker;

  public function testAuthorCanEditItsPost()
  {
    $user = User::factory()->create();
    $post = Post::factory()->for($user, "author")->create();

    $this->actingAs($user)
      ->post(route("post.update", $post), [
        "title" => $this->faker->name(),
      ])
      ->assertValid()
      ->assertSessionHas("success", "Post updated.");
  }
}
```

## 7. Assert a validation error message is returned

```php
namespace Tests\Feature;

use App\Models\Comment;
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class PostControllerTest extends TestCase
{
  use WithFaker;

  public function testAuthorSeeErrorIfEditingPostWithSameNameAsOneOfItsOtherPost()
  {
    $user = User::factory()->create();
    $post = Post::factory()->for($user, "author")->create();
    $name = $this->faker->name();

    Post::factory()
      ->for($user, "author")
      ->create([
        "title" => $name,
      ]);

    $this->actingAs($user)
      ->post(route("post.update", $post), [
        "title" => $name,
      ])
      ->assertInvalid([
        "title" => "The title has already been taken.",
      ]);
  }
}
```

## 8. Assert an authorization

```php
namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class PostControllerTest extends TestCase
{
  use WithFaker;

  public function testUserCannotSeePostHeOrSheDidNotCreate()
  {
    $user = User::factory()->create();
    $otherUser = User::factory()->create();
    $post = Post::factory()->for($otherUser, "author")->create();

    $this->actingAs($user)
      ->get(route("post.show", $post))
      ->assertForbidden();
  }
}
```

## 9. Assert a command ended without errors

```php
namespace Tests\Feature;

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

class NotifyFreePlanEndsTest extends TestCase
{
  public function testUserCannotSeePostHeOrSheDidNotCreate()
  {
    $user = User::factory()
      ->count(15)
      ->create([
        "plan" => "free",
        "subscribed_at" => Carbon::now()->subDays(15),
      ]);
  
    $this->artisan("notify:free-plan-ends")
      ->assertSuccessful()
      ->expectsOutputToContain("15 users notified.");
  }
}
```

## 11. Assert a json content is sent

```php
namespace Tests\Feature;

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

class TaskControllerTest extends TestCase
{
  use WithFaker;

  public function testAuthenticatedUserCanCreateTask()
  {
    $user = User::factory()->create();
    $name = $this->faker->name();
    $dueAt = $this->faker->date();
  
    $this->actingAs($user)
      ->post(route("task.store", [
        "name" => $name,
        "due_at" => $dueAt,
      ])
      ->assertJson([
        "name" => $name,
        "dueAt" => $dueAt,
      ]);
  }
}
```

## Conclusion

I hope this post gave you the motivation to start testing if you did not get used to it yet! When you feel ready, head on the [Laravel Test documentation](https://laravel.com/docs/9.x/testing), this is a gold mine and you will find other awesome assertions.

Please also share your snippets in the comments section if you also have some quick and easy ways to test things in Laravel 🙏

Happy testing 🧪