Exploring Laravel Test Assertions: A Complete Developer’s Guide

Laravel Test Assertions

Writing tests for your Laravel applications is crucial, but how do you know if your tests are working properly?

Laravel testing can get complicated, and sometimes, it’s hard to know where to start or which tools to use. You want to make sure your app behaves as expected, but testing everything manually is time-consuming and inefficient.

That’s where Laravel Test Assertions come in. It allows you to write tests that check specific behaviors of your app, ensuring everything works smoothly. In this blog, we’ll explore different test assertion types and see how Laravel development experts use them effectively to make your testing process faster, easier, and more reliable.

What are Test Assertions?

In simple words, Laravel assertions are checks in your tests to determine whether something is true or not. If an assertion passes, the test continues. If it doesn’t, Laravel lets you know that something went wrong.

You can use assertion statements such as assertStatus (200) to check that the response status code is okay when testing load accuracy for a page.

They also confirm whether the page returned the right status code, that some text appeared on the page when it should, or that a user has been added to the database.

Laravel assertions are built on PHPUnit, but they are more readable. Assertions catch problems early and give you confidence that the code is working well.

Getting Started with Laravel Testing

Before we jump into test assertions, let us first understand how testing works in Laravel. The framework includes everything you need to start writing and running tests right away. From generating test files to running them in the terminal, everything is super smooth and developer-friendly when it comes to working with Laravel.

Setting Up

Laravel comes with PHPUnit pre-installed, so you don’t need to add anything extra to start testing. Everything is ready to go right after installing Laravel.

To run your tests, simply open your terminal and use:

php artisan test

This command runs all the test files located inside the tests directory. Laravel will display the results in a clean, easy-to-read format so you can quickly spot any issues.

If you prefer using PHPUnit directly, you can also run:

vendor/bin/phpunit

But the php artisan test is more readable and beginner-friendly, especially when starting out.

Creating a Test

Laravel makes it easy to generate test files using Artisan commands.

To create a new test, just run:

php artisan make:test SampleTest

A file named SampleTest.php will be created by default in the tests/Feature directory

If you want to create a unit test instead of a feature test, you can specify it like this:

php artisan make:test SampleTest --unit
  • Feature tests are useful for larger parts of the application, such as the routes, controllers, or full workflows.
  • On the other hand, unit tests are responsible for testing individual functions, methods, or classes.

Each test class will have a simple method for testing, where you start adding your assertions. Now, this just means you’re ready to start testing the features of your application.

When you are doing this, you can set upon writing specific checks with assertions, which will make your tests very functional and deeper in reliability.

Once you’re familiar with the basics, you can move on to writing specific checks using assertions to make your tests more powerful and reliable.

Need Help With Laravel Testing? We can Help!

Types of Laravel Test Assertions

Laravel offers a variety of test assertions to help you verify different parts of your application, from checking HTTP responses to validating database records. Each type is built to test specific behaviors, making your tests more accurate and easier to understand.

Response Assertions

These are used to check what your application returns when someone visits a route or submits a form.

  • assertStatus(200): Confirms the HTTP status code is 200 (OK).
$response->assertStatus(200);
  • assertSee(‘Welcome’): Makes sure the given text appears in the response.
$response->assertSee('Welcome');
  • assertDontSee(‘Error’): Checks that the response does not contain the specified text.
$response->assertDontSee('Error');
  • assertRedirect(‘/dashboard’): Verifies the response is redirecting to a specific path.
$response->assertRedirect('/dashboard');
  • assertExactJson([‘status’ => ‘success’]): Validates the response has exactly this JSON structure.
$response->assertExactJson(['status' => 'success']);

View Assertions

These are useful when your route or controller returns a Blade view. You can check if the right view is loaded and what data it includes.

  • assertViewIs(‘home’): Confirms the response is rendering the ‘home’ view.
$response->assertViewIs('home');
  • assertViewHas(‘user’): Checks that a variable called ‘user’ is passed to the view.
$response->assertViewHas('user');
  • assertViewHasAll([‘user’, ‘posts’]): Ensures multiple variables are present in the view.
$response->assertViewHasAll(['user', 'posts']);

Session Assertions

When your app stores data in the session (like flash messages), these assertions help you test that behavior.

  • assertSessionHas(‘message’): Confirms the session has a specific key.
$response->assertSessionHas('message');
  • assertSessionHasErrors([’email’]): Verifies that a validation error exists for ’email’.
$response->assertSessionHasErrors(['email']);
  • assertSessionMissing(‘status’): Checks that a session key does not exist.
$response->assertSessionMissing('status');

Database Assertions

These let you check whether the database contains (or doesn’t contain) specific records after certain actions.

  • assertDatabaseHas(‘users’, [’email’ => ‘test@example.com’]): Confirms a user with this email exists.
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);
  • assertDatabaseMissing(‘orders’, [‘status’ => ‘cancelled’]): Makes sure a certain record is not in the table.
$this->assertDatabaseMissing('orders', ['status' => 'cancelled']);
  • assertSoftDeleted(‘posts’, [‘id’ => 1]): Verifies that the post is soft deleted.
$this->assertSoftDeleted('posts', ['id' => 1]);

Authentication Assertions

Used to check whether a user is logged in, logged out, or if the authenticated user matches a specific record.

  • assertAuthenticated(): Confirms that a user is logged in.
$this->assertAuthenticated();
  • assertGuest(): Checks that no user is authenticated.
$this->assertGuest();
  • assertAuthenticatedAs($user): Verifies that the currently logged-in user matches the given user.
$this->assertAuthenticatedAs($user);

Miscellaneous Assertions

These don’t fall into a specific category but are still useful in many situations.

  • assertTrue($condition): Basic check to confirm a value is true.
$this->assertTrue(true);
  • assertFalse($condition): Validates that a value is false.
$this->assertFalse(false);
  • assertEquals(5, $value): Confirms two values are equal.
$this->assertEquals(5, $value);

These assertion types cover the most common areas of Laravel testing. As you begin writing more tests, combining these assertions helps build strong and reliable test coverage for your application.

Once you get comfortable with these assertion types, you’ll be able to write clean and meaningful tests that clearly show what your application should do.

Laravel Test Assertions in Action

Testing in Laravel helps ensure your application behaves as expected under various conditions. Let’s look at a few real-world scenarios where Laravel test assertions can make a big difference.

Testing a Product Purchase Flow

Imagine you’re testing an eCommerce website. You want to confirm that when a user purchases a product, the order is successfully stored in the database, the user is redirected to a confirmation page, and a success message is displayed.

public function testProductPurchase()
{
    // Create a user and a product
    $user = User::factory()->create();
    $product = Product::factory()->create();
    // Act: Log in, add product to cart, and purchase
    $response = $this->actingAs($user)
                     ->post('/purchase', ['product_id' => $product->id]);
    // Assert: Check if the order was added to the database
    $this->assertDatabaseHas('orders', [
        'user_id' => $user->id,
        'product_id' => $product->id,
    ]);
    // Assert: Check if the user was redirected to the confirmation page
    $response->assertRedirect('/order/confirmation');
    // Assert: Check if the success message is in the session
    $response->assertSessionHas('message', 'Purchase successful!');
}

Explanation:

  • assertDatabaseHas: Confirms the order is saved in the database.
  • assertRedirect: Ensures the user is sent to the confirmation page.
  • assertSessionHas: Verifies that the success message exists in the session.

Testing User Authentication

You may want to ensure that only logged-in users can access certain parts of your application, such as the user profile page. Here’s how you can test that:

public function testUserProfileAccess()
{
    // Create a user
    $user = User::factory()->create();
    // Act: Try to access the profile page without being authenticated
    $response = $this->get('/profile');    
    // Assert: Check if the user is redirected to the login page
    $response->assertRedirect('/login');
    // Act: Now log in as the created user and access the profile page
    $response = $this->actingAs($user)->get('/profile');
    // Assert: Check if the profile page loads successfully
    $response->assertStatus(200);
    $response->assertSee($user->name);
}

Explanation:

  • assertRedirect: Checks that non-authenticated users are redirected to the login page.
  • assertStatus: Verifies the profile page loads correctly for authenticated users.
  • assertSee: Ensures the user’s name appears on the profile page.

Testing Email Notifications

Another common use case is testing email notifications. Let’s say your application sends a welcome email after a user registers. You can test this with Laravel’s built-in mail assertions.

public function testWelcomeEmail()
{
    // Create a user
    $user = User::factory()->create();
    // Act: Register a new user
    $response = $this->post('/register', [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'password' => 'password123',
    ]);
    // Assert: Check if a welcome email was sent
    $this->assertTrue($this->mailer->hasSent(Mail::to($user->email)));
}

Explanation:

  • assertTrue: Ensures that the welcome email was sent to the newly registered user.

Testing Database Changes After a Delete Operation

Testing delete operations ensures that your app behaves correctly when records are removed. For example, you may want to test that when a product is deleted, it no longer exists in the database.

public function testProductDeletion()
{
    // Create a product
    $product = Product::factory()->create();
    // Act: Delete the product
    $response = $this->delete('/products/' . $product->id);
    // Assert: Check that the product was deleted from the database
    $this->assertDatabaseMissing('products', ['id' => $product->id]);
    // Assert: Check if the user was redirected back
    $response->assertRedirect('/products');
}

Explanation:

  • assertDatabaseMissing: Verifies that the product no longer exists in the database after the deletion.
  • assertRedirect: Ensures the user is redirected to the product list after deletion.

These examples show how Laravel test assertions can be applied to common tasks, such as validating user actions, checking database changes, and testing emails. As you build more tests, combining these assertions allows you to create robust tests that safeguard your application from unexpected errors.

Best Practices for Laravel Test Assertions

Writing efficient tests is important for a healthy and reliable application. Following best practices in performing Laravel test assertions ensures that this event is likely to make your tests safe, readable, and provide maximum value. Here are a few things to keep in mind:

Keep Tests Simple and Focused

A test typically focuses on a single behavior or functionality. This makes it easier to understand what’s being tested and ensures tests remain isolated from each other. If you test too much in a single test, debugging becomes harder when something fails.

Example:

Instead of testing user authentication, profile updates, and email notifications in a single test, break them into separate, focused tests.

// Test: User Login
public function testUserLogin() {
    // code
}
// Test: Profile Update
public function testProfileUpdate() {
    // code
}
// Test: Welcome Email
public function testWelcomeEmail() {
    // code
}

Test the Happy Path First

Start with happy path testing (when everything works as expected). It helps you ensure that the basic functionality is working as expected before getting into error scenarios. Happy path testing is much easier and quicker. Here’s an example:

public function testProductCreation() {
    // Test that a product is successfully created
    $response = $this->post('/products', [
        'name' => 'Product 1',
        'price' => 100,
    ]);
    $response->assertStatus(201);
    $this->assertDatabaseHas('products', ['name' => 'Product 1']);
}

Avoid Over-Mocking

Mocking is a powerful test-isolation tool, but excessive use makes tests hard to understand and maintain. Always mock only when necessary, especially if you are testing code that interacts with external services such as APIs or databases.

Example:

Instead of mocking user authentication in every single test, Laravel provides the actingAs method, which is effective for simulating authenticated users.

$response = $this->actingAs($user)->get('/profile');
$response->assertStatus(200);

Use Factories for Test Data

With Laravel’s model factories, fake data is generated very conveniently for testing. So, it keeps the tests clean and eliminates the need for manually creating database records.

Example:

$user = User::factory()->create(); // Creates a user using a factory

Factories make data setup convenient and consistent across tests. 

Clean Up After Tests

Laravel’s testing environment is isolated by default, but you should still make sure any data created during tests is cleaned up. Use RefreshDatabase to ensure the database resets between tests so that it can begin anew for each test run.

use Illuminate\Foundation\Testing\RefreshDatabase;
class ProductTest extends TestCase
{
    use RefreshDatabase;
    public function testProductCreation() {
        // Test code
    }
}

This practice helps prevent tests from depending on each other’s state.

Test All Types of Responses

When testing, ensure you cover all possible response scenarios—successful, error, and edge cases. For example, test how the application responds to both valid and invalid user input to ensure error handling works correctly.

Example:

// Test valid form submission
$response = $this->post('/register', ['email' => 'test@example.com']);
$response->assertStatus(201);
// Test invalid form submission
$response = $this->post('/register', ['email' => 'invalid-email']);
$response->assertSessionHasErrors(['email']);

Keep Tests Small and Fast

Your test suite should run quickly, especially as your application grows. Small, fast tests allow you to run them frequently and spot issues early. Avoid complex database operations and heavy logic in your tests unless absolutely necessary.

Write Descriptive Test Names

A good test name clearly describes what the test does. This improves readability and makes it easier to understand the purpose of a test just by looking at the name.

Example:

public function testUserCanUpdateProfile()
{
    // code
}

The name testUserCanUpdateProfile clearly communicates what’s being tested.

Use Assertions for Edge Cases

While the happy path is essential, you must also test edge cases. Ensure you cover scenarios like invalid data, empty fields, and unexpected input. This helps improve the robustness of your application.

Example:

public function testEmptyEmailField()
{
    $response = $this->post('/register', ['email' => '']);
    $response->assertSessionHasErrors('email');
}

By following these best practices, you ensure that your tests remain effective, maintainable, and provide valuable insights into the quality of your application. Well-tested applications are easier to debug, more stable, and offer a better experience for your users.

Advanced Tips

Once you’re comfortable writing basic tests in Laravel, these advanced tips can help you improve efficiency and coverage even further.

Use Data Providers for Reusable Test Cases

Instead of writing multiple similar tests, use data providers to run the same test with different inputs and expected outcomes.

/**
 * @dataProvider statusCodeProvider
 */
public function testResponseStatusCodes($route, $expectedStatus)
{
    $response = $this->get($route);
    $response->assertStatus($expectedStatus);
}
public function statusCodeProvider()
{
    return [
        ['/home', 200],
        ['/admin', 302],
        ['/invalid-page', 404],
    ];
}

Create Custom Assertions

If you’re repeating the same assertions across many tests, consider writing custom ones. This keeps your tests clean and expressive.

public function assertIsSuccessfulResponse($response)
{
    $response->assertStatus(200);
    $response->assertSee('Success');
}

Use Parallel Testing for Speed

Laravel supports running tests in parallel to speed up large test suites. You can enable this with:

php artisan test --parallel

It’s a huge time-saver for larger applications.

Combine Multiple Assertions Smartly

Instead of breaking your logic into separate tests, you can combine related assertions in a logical flow. Don’t overdo it. Keep them relevant and focused.

Using these advanced techniques helps you scale your tests as your application grows without making things complicated.

Need Custom Laravel Features?

FAQs about Laravel Test Assertions

What’s the difference between unit tests and feature tests in Laravel?

Unit tests focus on testing small, isolated pieces of logic, like a specific method or class, without relying on the database or external dependencies. Feature tests, on the other hand, test complete flows or routes and often interact with the database, middleware, and more.

Can I test APIs built with Laravel using assertions?

Yes, Laravel provides powerful tools for API testing. You can use assertions like assertJson, assertJsonFragment, and assertStatus to check API responses, structure, and content. These make it easy to verify that your endpoints return the expected results under various scenarios.

How do I test validation rules in Laravel forms or controllers?

You can test validation by submitting requests with both valid and invalid data and then asserting the results. For invalid data, use assertSessionHasErrors() to confirm specific validation messages are triggered.

What should I do if my tests are failing inconsistently?

Inconsistent or “flaky” tests often point to problems with shared state, asynchronous processes, or database issues. Make sure to use Laravel’s RefreshDatabase trait, avoid relying on hardcoded data, and isolate each test.

Is it possible to generate test scaffolding automatically in Laravel?

Yes, Laravel allows you to quickly generate test files using Artisan commands like php artisan make:test ExampleTest. You can also pass flags like –unit or –pest to generate specific types. This saves time and provides a structured starting point for writing your tests.

Let’s Summarize

Laravel test assertions are powerful tools that help you write reliable, maintainable tests. They make sure your application works as expected and give you confidence in your code.

Whether you’re testing routes, APIs, views, or database records, Laravel gives you an expressive way to do it all. If you’re building a small application or managing a large project, writing strong, meaningful tests helps ensure your code stays solid and maintainable.

If you’re planning to implement efficient testing workflows in your Laravel project or want to enhance the reliability of your application, consider partnering with a Laravel development company. The right team can help you set up, scale, and maintain high-quality test coverage.

author
Mayur Upadhyay is a tech professional with expertise in Shopify, WordPress, Drupal, Frameworks, jQuery, and more. With a proven track record in web development and eCommerce development.

Leave a comment