21 Aug 2019
What is TDD in Laravel? The basic idea and implementation of TDD in Laravel
TDD: Basic Idea
The basic idea of the TDD is your Laravel Development should be dependent on test cases and not the other way around. Before writing a single line of code for development, you should have all your test cases done and dusted for that piece of code. Now, test cases are divided into two categories, one is Unit Tests and second is Feature tests.
Unit Test: In unit testing, your tests your classes like Models, Facades, Repos, crud operations.
Feature Test: You test your logic of the code, your assertions etc.
Now some of you may have this question, like, “How do I know what kind of test cases should I write before starting the development?” Well to answer that question, I would recommend following the “A walking skeleton” approach which is mentioned in one of the software books, what this means is you string up a skeleton with as many as small pieces which are utmost necessary. For example, let’s say that someone has asked you to make a Medicine Inventory Management System. Now, what is the most basic thing in this system? Yes, you guessed it right, it is Medicine. But before starting the project let me make one thing clear, there will be NO FRONTEND in this reading, only BACKEND. Now, let’s make a list of the required to build a small functionality of creating and updating Medicine.
1. Route to add/update the data
2. Validation of the fields
3. Method to add/update the data.
4. Return the data is you need to.
Now, after we have defined our development requirements let’s start writing test case for this but before that, we need to do some basic setup for Laravel project which I assume you are familiar with but here are the steps, follow them and you are good to go.
1. Create a project by command “composer create-project laravel/laravel medicine.” This will create a brand new project at your hand. Now make some changes in your project file as below. In file ‘phpunit.xml’ add
server name="DB_CONNECTION" value="sqlite"
server name="DB_DATABASE" value=":memory:"
Here we will be using DB connection as “sqlite” and DB as memory, no actual database. Now in .env file add “sqlite” as your
DB_CONNECTION and remove the remaining DB configurations as we won’t be needing them.
2. Create a test case file by this command“php artisan make:test MedicineTest” and this will create a MedicineTest.php file in the tests/Feature/ folder. To create a test case file in unit folder use this command“php artisan make:test MedicineTest –unit“. Now let’s get our hands dirty with code. Use the below code in your test file.
* This test case will test if the medicine can be added or not
public function test_a_medicine_can_be_added()
$response = $this->post('/medicine', [
'title' => 'Paracetamol',
'expiration_date' => '2019-07-08 15:18:54'
- Shall we run this code? why not! But there is one small problem we don’t know how to run a test case. Here is the command in Laravel how to run a test case :`php vendor/phpunit/phpunit/phpunit tests/<dir>…/<test-case-file>`.
- Run the test case you created and see what is the output in the console. It will be something like this.Run the test case you created and see what is the output in the console. It will be something like this.
- FAILED. Yes, it will get failed coz we haven’t done any development yet. So the assetOK() method will check if you get 200 as a status code in the response, but you didn’t because the route which you are trying to hit isn’t created yet and you will get the 404 status code in response. So what are you waiting for? Go ahead and create the route. Add the following code in the web.php file.
- After creating the route, run the test case and see what error message you get. You will get this error of MedicineController does not exist.
- Please create the Controller and run the test case again and you will get the following error.
- Please create the store method in the controller and run the test case again and see the output console. You will get the following error. This error says that the Model of medicine is not available which we are calling in assertCount method.
- Please create the model named “Medicine” with the migration file and run the test case again. You will get the following error. This error shows that we are expecting the return count to be 1 but as there is no code in the controller method returning count is 0, so code along in the
Let’s add some code in the store method of MedicineController. Add the following code the method and run the test case again.
* @desc This function will validate the request data and save it in the database
* @param Request $request
public function store(Request $request)
$data = $request->validate([
'title' => 'required',
'expiration_date' => 'required'
- Run the test case and see what is the output. VOILA! Test case runs successfully.
- Here, we first developed the test case and then we moved on to the development of that feature. Here I conclude my first part of this tutorial. In the next part, we will walk through the various methods of the test cases and learn about and how we will implement them in our test case. You refer to the following link https://laravel.com/docs/5.8/http-tests#available-assertions to go through the assertions which we will be using in future and it will help you in learning the TDD. This tutorial series will be just a starting point for you to work with, please refer others resources also. Below you can find the methods which we used in the test cases.
Things to Remember:
1. $this->withoutExceptionHandling(); // will show the full error log
2. $this->withExceptionHandling(); // will only show the specific error message
3. $this->assertOk(); // will check the return status of request is 200
4. $this->assertSessionHasError(‘key’); // will check the return error array has this key,
usedin the validation test case
5. $this->assertEquals($expected, $actual); // this will check if the expected o/p and the
actual o/p after execution are same
6. For PHPUNIT to know your test, its either you put the /** @test */ annotation on the docblock
or test_ as a prefix.