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
After successful installation change the directory to laravel_testing
cd laravel_testing
-- Run
php artisan serve
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.