Best practices with example to write feature test in laravel
Tags: laravel, webdev, test, beginners
Published: 2023-08-25 03:13:42

Hey there! When it comes to writing feature tests in Laravel, there are some really helpful best practices you can follow to make sure your tests stay effective and easy to maintain. Check out this example below to see how you can structure and write a feature test in Laravel, and keep these best practices in mind as you go!

1. Organize your tests:
    - Place your tests in the **`tests/Feature`** directory.
    - Create a separate file for each feature you're testing.
    - Name your test files with descriptive names that indicate the feature being tested, such as **`UserRegistrationTest.php`**.
2. Use descriptive test names:
    - Give your test methods descriptive names that clearly explain what aspect of the feature is being tested.
    - Use a naming convention like **`test_<WhatIsBeingTested>_<ExpectedBehavior>`**.
3. Set up the test environment:
    - Use Laravel's built-in testing tools to set up and tear down your test environment.
    - Laravel provides the **`RefreshDatabase`** trait, which automatically migrates the database before each test and rolls back the changes after each test.
4. Use assertions and helpers:
    - Laravel provides a wide range of assertion methods and helpers to simplify your tests.
    - Use methods like **`assertStatus()`**, **`assertRedirect()`**, **`assertSee()`**, and **`assertDatabaseHas()`** to verify the expected behavior of your application.
5. Mock external dependencies:
    - Use Laravel's mocking facilities to mock external dependencies such as API calls or database interactions.
    - This ensures that your tests focus on the specific feature being tested, rather than relying on real-world dependencies.
6. Arrange, Act, Assert (AAA) pattern:
    - Structure your tests using the AAA pattern: Arrange, Act, and Assert.
    - Arrange: Set up the necessary preconditions for the test.
    - Act: Perform the actions or operations that you want to test.
    - Assert: Verify that the expected results or behavior occurred.

Here's an example of a feature test for a user registration feature:

```jsx
<?php

namespace Tests\Feature;

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

class UserRegistrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_register()
    {
        // Arrange
        $userData = [
            'name' => 'John Doe',
            'email' => 'johndoe@example.com',
            'password' => 'password123',
        ];

        // Act
        $response = $this->post('/register', $userData);

        // Assert
        $response->assertStatus(302);
        $response->assertRedirect('/home');
        $this->assertDatabaseHas('users', ['email' => 'johndoe@example.com']);
    }
}
```

In this example, we have a test named **`test_user_can_register`**. It sets up the user data, sends a POST request to the **`/register`** endpoint with the data, and then asserts the expected behavior: a successful redirect to **`/home`** and the presence of the user in the database.

Remember to tailor your tests to your specific application and features, and aim for clear and concise tests that cover the essential aspects of the feature being tested.

By Using Factory

Using factories in Laravel is a common practice for generating fake data to use in tests. Factories make it easy to create test objects with realistic data and can help streamline the process of setting up test scenarios. Here's an example of how you can use factories in a feature test in Laravel:

1. Define a Factory:
    - Create a factory class for the model you want to generate fake data for.
    - Use the **`factory`** function to define the factory and specify the model class.
    - Within the factory definition, use the **`Faker`** library to generate random data for each field.
2. Use the Factory in Your Test:
    - Within your test method, call the factory to create instances of the model.
    - Customize the generated data as needed to match the specific test scenario.
    - Use the created instances in your test assertions.

Here's an updated example of a feature test for a user registration feature that utilizes a factory:

```jsx
<?php

namespace Tests\Feature;

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

class UserRegistrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_register()
    {
        // Arrange
        $userData = [
            'name' => 'John Doe',
            'email' => 'johndoe@example.com',
            'password' => 'password123',
        ];

        // Create a user using the factory
        $user = User::factory()->create($userData);

        // Act
        $response = $this->post('/register', $userData);

        // Assert
        $response->assertStatus(302);
        $response->assertRedirect('/home');
        $this->assertDatabaseHas('users', ['email' => 'johndoe@example.com']);
        $this->assertNotNull($user->fresh()->email_verified_at);
    }
}
```

In this example, we have added the **`use App\Models\User;`** statement to import the **`User`** model. We then create a user using the factory with the provided **`$userData`**. This will generate a new user instance with the specified data and insert it into the database.

Using factories can help make your tests more flexible and scalable. You can easily generate multiple instances of models with varying data to test different scenarios or test cases. Additionally, if your model relationships require additional data, you can use the factory's associations to automatically generate related models.

Remember to define your factories in the appropriate factory classes within the **`database/factories`** directory of your Laravel application.

---

When working on large projects, it's important to establish a well-organized folder structure for your feature tests. A clear and logical folder structure makes it easier to locate and maintain your tests, especially as the number of tests grows. Here's a recommended folder structure pattern for feature tests in a large Laravel project:

**tests**

- **Feature**
    - **Authentication**
        - LoginTest.php
        - LogoutTest.php
        - RegistrationTest.php
    - **UserManagement**
        - CreateUserTest.php
        - UpdateUserTest.php
        - DeleteUserTest.php
    - **OrderManagement**
        - CreateOrderTest.php
        - UpdateOrderTest.php
        - CancelOrderTest.php
    - **ProductManagement**
        - CreateProductTest.php
        - UpdateProductTest.php
        - DeleteProductTest.php
    
    Let's break down the folder structure:
    
    - The **`tests`** directory is the main directory for all your tests.
    - Inside the **`tests`** directory, you have a subdirectory named **`Feature`** to store your feature tests specifically.
    - Within the **`Feature`** directory, create subdirectories based on the major features or functional areas of your application. For example, you could have subdirectories like **`Authentication`**, **`UserManagement`**, **`OrderManagement`**, **`ProductManagement`**, etc.
    - Inside each subdirectory, create individual test files for each specific feature or functionality. For example, in the **`Authentication`** directory, you might have test files like **`LoginTest.php`**, **`LogoutTest.php`**, **`RegistrationTest.php`**, etc.
    - Repeat this pattern for each major feature or functional area of your application.
    - If your project also includes unit tests, you can create a separate **`Unit`** directory at the same level as the **`Feature`** directory.
    
    When you organize your feature tests this way, you can find and move through your tests based on what you're testing. This structure helps keep things separate and makes it easier to manage and run tests for specific parts of your application.
    
    Make sure to adjust this folder structure to fit your project's needs and design. The example is just a starting point, and you can change it based on what your project requires.

Keep up the good work! Keep coding! Keep on exploring!