Simple TDD in Laravel with 11 steps

Most web developers cringe when they hear of (test-driven development). Well, I did when I was asked to program with TDD first.

When you are starting off, it feels overwhelming. If you resist it, it will make it harder for you to learn it. So what should you do? Embrace it. Well, it has the reason why it is there. There will always be debates on technology, whether the programming language or the process you will use to develop software.

Some may agree that XP or Xtreme Programming is better than TDD and vise-versa. It will really boil down to which path you would like to take as a programmer especially if you are a team lead. So choose wisely.

Note: This is TDD for API responses. If you want the Feature testing with Laravel blade, head up on this article. If you are into NodeJS, we can also do TDD!

UPDATE: If you want to know about TDD in package development in Laravel, I have written a new article here.

That is too much intro; let’s get to work!

On your root directory, update your file with:

So it will look like this:

We just need to test in-memory so it will be faster. We will use database and for the database. I have to make the debug to false since we need only the actual errors to assert. Increasing memory limit may be needed in the future when your actual calls become expensive.

Make sure your base if prep for it.

We need to add the trait, so in every run of the test, the migration files are also being run. You may also notice that we have the andmethods that are needed to complete that cycle of the application during the test.

Just like what Uncle Bob said, you do not have the authority to write a code (implementation), unless you write the TEST first.

So, let us write our test.

For PHPUNIT to know your test, its either you put the /** @test */ annotation on the or test_ as a prefix.

On this test, we check if we can create an article.

We assert if the application will give us a status 201 and if it will respond with the correct data.

But why do we need to assert the response status FIRST before the response body or payload? It is important because the response status code determines the type of payload we return. If we have 2xx response, we either return the data being asked but if we have 4xx, we return an error response. It should go hand in hand so that the consumer of the API will not be scratching their heads.

For Laravel, since it is using an , data are best to persist in the database when created.

After we created our first test, run phpunitorvendor/bin/phpunit

So when we run phpunit, it ! Is this good? Yes it is! Because we ran on the second rule of that it should after creating your test.

Let us examine first why did it fail.

We assert in the test that it should return a 201 but it returned 404. Why?

Most of you might know WHY but for the others, it is because the URL is not yet built. The [POST] is not yet existing thus throwing 404.

What do we need to do?

Let us create the URL and see what happens.

Go to your file and create the URL. When you create url routes in the , it automatically prefixes it with

You can either run:

php artisan make:controller ArticlesApiController —-resource

or you can create it manually. I created it manually. The requests goes toArticlesApiController’s, method.

So let’s debug and see if the call can reach in that part of our application and run phpunitagain.


It is prompting us the string in the terminal! This means it is catching our test.

Do not forget to validate the data going to your database. So we create a new class called to handle validation.

And what does it contain? The validation rules of course!

This is good because we can create later on another test to see if it is catching the validation errors, but that will be on a separate post to make this tutorial simple.

Remember that a certain JSON structure should be returned so we know that the data is created in the database. So we need to return the Article object to satisfy our test.

You may notice that the is highlighted. This is because the IDE (PhpStorm) cannot locate that class. So, let’s create it!

On this Article class, you have to define the and fields. Once you are done with it, check again your controller and import the Article class.

If you notice, the class is not highlighted anymore since the IDE can locate already the file.

We are getting almost there! The test is set up correctly, the URL is already built and accessible, a controller to catch it is in place as well and the class to model our database tables are ready. Now, let us try to run again the phpunit.

It fails. Again. :( Good or Bad? Well, good and bad. Good because our assertion for a status 201 has changed from 404 to 500 (if you notice that).

And the bad, well, it fails and we need it to pass right? When you want to debug and see what is the application is really throwing, you can do this in your test. Just add ->dump() method after the post request.

You can further debug the output of our POST request. It might have have all the information you need. If it still doesn’t give you a hint on what is happening, you can rely on the file.

So let us examine why the error has turned into 500.

Well, we are trying to insert data to a table that is thus the application is complaining that there is no table to insert the data into.

You just need to run the laravel command:

And it will create a migration files under


By default, it will only have the ID and the Timestamp fields. It is up to you to fill it up with the required fields.

We’re almost there! We promise this to be 11 steps and we are on step 10! You need to pat your back for making this far!

Ooops, again? Whyyyyyyy??? But if you examine carefully, you are in the right track! The status code has changed again from 500 to 200. Status 200 is a good sign because it means it is returning something successfully after the POST request! It’s just doesn’t match we need. We need the 201 code to know that an actual post is inserted in the database. So we need to just modify our controller to:

CONGRATULATIONS! You made it pass and that is the THIRD RULE of Uncle Bob!

This is just the simple implementation of . There are other approaches on this that I might cover in the future like the . The repository pattern is best implemented with DDD or domain driven development.


Edit: You can check the refactor using the repository pattern here.

There are still a lot to cover but this should get your feet wet on TDD. Comments are welcome!

Thank you.




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jeff Simons Decena

Senior Fullstack Engineer, React Native developer and HL7 Health Informatics professional