Test Driven Development and its implementation in a Laravel application - Part 1

Test Driven Development and its implementation in a Laravel application - Part 1

Exploring test driven development in a Laravel application

At the heart of every project is the assurance that its functionalities work end to end as expected or designed. Test-driven development is a development approach where a developer writes test cases (failing and passing) as per the requirement of the application while building it. For example, a test case can be written to ascertain the behavior of a method as it would happen when a user makes a request that will trigger the method.

In some cases, team engineers might prefer to test the code behavior in real time while building the application. For example, after writing codes for a post method, a form interface is designed to test its functionality.

No confusion here, it all depends on the team's decision, project needs, or the project setup.

However, as a developer, the best way to overcome such inconsistency in codebases since no rule says "every developer must write test cases for their application" is to arm yourself with both skills

Test: a way to ascertain the quality(ies) of something/functionality to confirm and approve that expected behavior or functionalities are in order.

Testing can be carried out manually, automatically or both.

Why do you need to test an application?

  • The brand name of a product is at stake when users keep encountering challenges.

  • Most times users (customers) are not patient with errors.

  • Helps to identify loopholes in code structure and setup.

  • Inform decision to plan for technical support backup in cases where the failure of a function is based on the environment where the application is hosted.

  • Provides guides on easy ways to communicate with the application because some issues are not code based. For example, in a situation where errors can be thrown if a user enters the alphabet as opposed to an expectation of a digit.

Test Driven Development

is a practice in software development that first ensure that test cases are written (failing and passing) before building the actual code for manual testing (developer/user testing) to ensure quality code and redesign where possible.

For example, I can create a test to fail if a post request is made as opposed to the expected get request and then have it pass when a get request is made.

Phases of Test-Driven Development

  • Write test cases for units

  • Review the code for failed or successful test cases

  • Redesign the code to ensure maximum optimization of code implementation

The cycle of TDD implementation is often denoted as "Red-Green-Refactor"

  • create a test case

  • (Red) execute for failed tests

  • (Green) execute for a successful test

  • (Refactor) Optimize the code to ensure quality and correctness

Benefits of Test-Driven Development

  • Quality code

  • Ease of code refactoring

  • Maintainability

  • Minimizes bugs

  • Increases team effectiveness and efficiency

  • Improves developer performance (develop and test endpoints without having to build user interfaces at first)

  • Create rooms for automated regression testing

  • In CI/CD tools can be integrated to execute the checks on the codebase using the test cases.

Disadvantages of TDD

Sometimes as team engineers/developers, we tend to place too much emphasis on certain practices in building software and end up creating multiple issues.

If the flex of the mental muscles of technical proficiency doesn't end in providing a solution it is useless.

  • Writing test cases for functionalities that don't need them because there are no laid down rules on when and when not to write test cases.

  • Prolongs time of developing small and sometimes large applications.

  • It doesn't eliminate the need for manual testing at the end.

  • Developers are armed with a variety of frameworks for writing test cases which can slow down the speed of development if a developer needs to change the framework for testing or rewrite the test cases.

  • Much cost will be incurred to write test cases for an existing or legacy codebase.

  • A codebase with test-driven development implementation is challenging to scale.

  • Requires an experienced developer with an understanding of the specific TDD implementation (set of tools) in the project.

  • Too much tweaking is involved in the test cases when the functionalities or behavior changes.

  • Most times it's a strenuous activity for developers to write test cases when there's a QA in the team without skills in how to write test cases.

Application testing in Laravel

One of the cool features in Laravel is the integration of PHPunit out of the box and a phpunit.xml to enable application testing. There are numerous functions shipped with the Laravel framework that allows you to expressively test your applications.

  • By default the test directory is part of the Laravel application scaffolding with two folders (directories) namely:

  • Feature

Feature tests may test a bigger piece of your code, like how numerous objects interact with each other or even a whole HTTP request to a JSON endpoint.

  • Unit

Unit tests isolate and concentrate on a relatively tiny section of your code. In actuality, a single method is probably the subject of most unit tests. Since tests within the Unit do not boot your Laravel application they'll not be able to access databases and other framework services.

Most of the test cases you'll write in your application generally should be feature tests, this gives a full assurance that the application is working end to end as expected

The Feature and Unit test directories contain an ExampleTest.php file respectively which comes by default with a new Laravel application. You can execute the vendor/bin/phpunit or php artisan test commands to run your tests.

  • Environment

Laravel by default set the configuration environment to testing because of the environment variables defined in the phpunit.xml file when running tests.

Other than the automatically configured session and cache to the array driver while testing, meaning no session or cache data will be persisted while testing you can as a matter of necessity define another testing environment. The testing environment variables may be configured in your application's phpunit.xml file, but make sure to clear your configuration cache using the config:clear Artisan command before running your tests!

The .env.testing Environment File

To configure a unique environment variable to be used when running PHPUnit tests or executing artisan commands with the --env-testing parameter you can create a .env.testing file in the root of the project.

The CreatesApplication Trait

By default, the CreatesApplication trait comes with a Laravel application scaffold that is applied to your application's base TestCase class. This trait contains a createApplication method that bootstraps the Laravel application before running your tests. You must leave this trait at its original location as some features, such as Laravel's parallel testing feature, depend on it.

Let's explore test-driven development and test cases implementation practically in a Laravel application

laravel new laravel_testing

OR

composer create-project laravel/laravel_testing

image.png

After successful installation change the directory to laravel_testing

image.png

cd laravel_testing

-- Run

php artisan serve

image.png

Laravel v9.39.0 (PHP v8.0.3)

  • Creating Tests

To create a new test case, use the make:test Artisan command. By default, tests will be placed in the tests/Feature directory:

php artisan make:test UserTest

If you want to create a test in the tests/Unit directory, you may use the --unit option when executing the make:test command:

php artisan make:test UserTest --unit

Test methods can be defined inside the UserTest generated

<?php

namespace Tests\Feature;

use Tests\TestCase;

class UserTest extends TestCase
{
    public function test_example()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

To run the UserTest, execute the vendor/bin/phpunit or php artisan test command from the terminal:

php artisan test or vendor/bin/phpunit

Arguments can be passed to the test command

php artisan test --testsuite=Feature --stop-on-failure

Running Tests In Parallel

Tests are executed sequentially within a single process by default in Laravel and PHPUnit. However, you can decide to run tests simultaneously across multiple processes to maximize time. To get started, ensure your application depends on the version ^5.3 or greater of the nunomaduro/collision package.

The Artisan command to run tests in Parallel also takes an argument to specify the number of processes.

php artisan test --parallel --processes=4

Parallel Testing & Databases

Laravel automatically handles creating and migrating a test database for each parallel process that is running your tests for a configured primary database connection. There's a suffix of process token on every process for test databases. For example, users_test_1, messages_test_2

To recreate the database test after it persists between calls to the test using this Artisan command

php artisan test --parallel --recreate-databases

Reporting Test Coverage

This feature requires Xdebug or PCOV.

php artisan test --coverage

The --coverage argument can be used to determine whether your test cases are covering the application code and how much application code is used when running your tests.

In the next part, we'll dive deep into Testing in a Laravel application...

Conclusion

Test-driven development helps to minimize code errors and ensures clean code. TDD can be implemented in a Laravel application by writing test cases to specify, confirm and validate code behavior/functionalities.

Twitter: Alemsbaja | Youtube: Tech with Alemsbaja to stay updated on more articles

Find this helpful or resourceful?? kindly share and feel free to use the comment section for questions, answers, and contributions.

Did you find this article valuable?

Support Alemoh Rapheal Baja by becoming a sponsor. Any amount is appreciated!