Many organizations are starting to use Test-Driven Development (TDD) as a way to improve their workflow: coding, testing, and designing. With TDD, you can create code that is correct, strong, and easy to maintain. In this article, we will look at how to implement TDD in Laravel.
What is Test-driven Development?
Test-Driven Development (TDD) is a method used in software development where you write tests before you write the actual code.
-The process starts by creating a simple test for a new feature and adding it to your test suite, then running the test to see it fail.
-After that, you write just enough code to make the test pass.
-Once the test is successful, refactoring the code helps improve and refine it.
-This cycle is repeated for every new feature or bug fix.
TDD effectively validates code functionality early in the development process, leading to more robust, maintainable software.
How to Implement TDD in Laravel?
Prerequisite:
To grasp the idea of Test Driven Development (TDD) in Laravel, you should know a few basics:
- PHPUnit: This is a testing framework for PHP, which helps you write tests in Laravel.
- Object-Oriented Programming (OOP): It’s important to understand basic OOP concepts such as classes, objects, inheritance, and interfaces.
- Software Testing: You should have a general idea of software testing types, including unit testing, integration testing, and acceptance testing, to better understand TDD.
Configuration:
Follow the steps below to configure TDD in Laravel.
Install Laravel: The first step is to install Laravel on your computer. You can do this by opening your terminal and typing the following command:
composer create-project --prefer-dist laravel/laravel <project-name>
Set Up Your Environment: Next, you need to set up your environment, which means configuring your database connection and other settings. To do this, copy the .env.example
file to a new file called .env
and then change the settings in the .env
file to fit your needs.
Install PHPUnit: PHPUnit is a testing tool for PHP, and you need to install it if you want to write and run tests in Laravel. To install PHPUnit, type the following command in your terminal:
composer require --dev phpunit/phpunit
Create a Test: To create a test, you need to make a new PHP file in the tests/
directory. Make sure the file name ends with Test.php
. In this file, you will write test methods that explain how your code should work. You can create a test file by running this command:
php artisan make:test ExampleTest
Run the Test: After you’ve created your test, you can run it using PHPUnit. To run all the tests in the tests/
directory, type this command in your terminal:
vendor/bin/phpunit
This will run all the tests in the tests/
directory and show the results in your terminal.
Implement TDD in Laravel
To begin, you’ll need to create a new Laravel project. You can do this by using the “Laravel new” command in your terminal.
After you set up your project, you can start writing tests for your code. Laravel has several testing tools that make it easy to test your application.
Let’s look at a basic example to understand how Test-Driven Development (TDD) works in Laravel. Imagine we want to build a simple blog application. We need features like creating a new blog post, retrieving a specific post, updating an existing post, and deleting a post. For this, we will use a PostController.
/**
* PostController
*/
class PostController extends Controller
{
/**
* Function is used to create the blog.
*
* @param Request $request
* @return JsonResponse
*/
public function createBlog(Request $request): JsonResponse
{
$post = Post::create([
'title' => $request->input('title'),
'body' => $request->input('body'),
'user_id' => Auth::id(),
]);
return response()->json($post, 201);
}
/**
* Get the blog information by id.
*
* @param integer $id
* @return JsonResponse
*/
public function getBlogById(int $id): JsonResponse
{
$post = Post::findOrFail($id);
return response()->json($post);
}
/**
* Function is used to update the blog.
*
* @param Request $request
* @param integer $id
* @return JsonResponse
*/
public function updateBlog(Request $request, int $id): JsonResponse
{
$post = Post::findOrFail($id);
$post->update([
'title' => $request->input('title'),
'body' => $request->input('body'),
]);
return response()->json($post);
}
/**
* Function is used to delete the blog.
*
* @param integer $id
* @return void
*/
public function deleteBlog(int $id)
{
$post = Post::findOrFail($id);
$post->delete();
return response()->noContent();
}
}
Now lets write a test case for creating blog
class BlogTest extends TestCase
{
/** @test */
public function a_user_can_create_a_blog_post()
{
// Arrange
$user = factory(User::class)->create();
$post = [
'title' => 'My first blog post',
'body' => 'This is the body of my first blog post.',
];
// Act
$response = $this->actingAs($user)->post('/createBlog', $post);
// Assert
$response->assertStatus(201);
$this->assertDatabaseHas('posts', $post);
}
}
In this example, we’re using Laravel’s built-in TestCase class, which has several methods that help with testing. We’re also using the Factory class in Laravel to create a new user.
The test method called a_user_can_create_a_blog_post has three main parts: Arrange, Act, and Assert.
In the Arrange part, we set up the data for a new blog post and create a new user.
In the Act part, we ask the user to send a POST request with the updated blog post data to the /posts endpoint.
Finally, in the Assert part, we check that the response status code is 201, which means the blog post was created successfully. We also make sure that the updated blog post data was saved in the database.
Let’s create a GET
method to fetch a specific blog post. Here’s an example of a test case:
class BlogTest extends TestCase
{
public function test_user_can_retrieve_single_blog_post()
{
// Arrange
$post = factory(Post::class)->create();
// Act
$response = $this->get("/getBlogById/{$post->id}");
// Assert
$response->assertStatus(200);
$response->assertJson([
'id' => $post->id,
'title' => $post->title,
'body' => $post->body,
]);
}
}
In this example, we’re making a new blog post using Laravel’s factory
method. We send a GET request to the /posts/{id}
endpoint to get the post, and then we check that the response has a status code of 200 (OK) and that the JSON data in the response matches the post data we created.
Next, let’s look at how to use the PUT method to update a blog post. Here’s an example test:
class BlogTest extends TestCase
{
public function test_user_can_update_blog_post()
{
// Arrange
$post = factory(Post::class)->create();
$newData = [
'title' => 'New title',
'body' => 'New body',
];
// Act
$response = $this->put("/updateBlog/{$post->id}", $newData);
// Assert
$response->assertStatus(200);
$this->assertDatabaseHas('posts', array_merge(['id' => $post->id], $newData));
}
}
In this example, we are making a new blog post using Laravel’s factory
method.
We will define some new information to update the post, send a PUT request to the /posts/{id} endpoint to change the post, and then check that the response shows a status code of 200 (OK) to confirm the update was successful, as well as verify that the post data has been changed in the database.
Now, let’s go on to the DELETE
method to remove a blog post. Here’s an example test:
class BlogTest extends TestCase
{
public function test_user_can_delete_blog_post()
{
// Arrange
$post = factory(Post::class)->create();
// Act
$response = $this->delete("/deleteBlog/{$post->id}");
// Assert
$response->assertStatus(204);
$this->assertDatabaseMissing('posts', ['id' => $post->id]);
}
}
Why Should We Use Test Driven Development?
- Improved Code Reliability: Developers can make sure their code works as it should, which helps to cut down on bugs and design errors, allowing them to build things correctly from the beginning.
- Quicker Feedback: It finds mistakes early, gives instant feedback, and makes it easier to fix problems, saving time and effort.
- More Confidence in Code Changes: A thorough set of tests gives developers confidence when they update or change the code, lowering the chances of creating new issues and ensuring the code meets its requirements.
- Easier Code Updates: It helps identify problems quickly and lowers the risk of new mistakes while updating the code, keeping the quality high and making sure the code runs properly.
- Better Teamwork: It promotes collaboration and efficiency within software development teams by setting clear goals and expectations, reducing confusion and misunderstandings, and making the development process smoother.