DEV Community

Cover image for Laravel Test Driven Development
Abishek Dongol
Abishek Dongol

Posted on

Laravel Test Driven Development

Test Driven Development (TDD) is an established technique which is used to develop software faster with a consistent pattern. TDD is also known as part of the Agile process. It helps to build software from dust to dawn which initially starts with a failed test before writing production code itself. There are numbers of ways to implement TDD like Unit Testing, Feature testing, etc. As of today, test driven development is an integral practice in many software projects. However, this practice is still difficult to master and presents significant challenges and risks if not applied right.

When TDD is implemented in any project it ensures to cover all required aspects to develop robust applications. The main concept of the TDD is using small tests to design bottom-up in an emergent manner and rapidly get some value while building confidence in the system.

The advantages of TDD are

  1. To build robust software.
  2. To avoid errors while introducing new features.
  3. To track project processes.
  4. To create self-documented software.
  5. To encourage simple designs and inspire confidence in code writing.
  6. To analyze feasibility of logic while implementing new features.

Implementation of TDD is dependent upon the developer. Developers can create their own way to test a single line of code. Every single line can cause a crash, every missing line will not meet requirements which need to be tracked down and solved one after another.But there are simply three steps required to follow the TDD process.
Alt Text

  1. Write a unit test that fails which shows a red signal.
  2. Write enough code to make the test pass with a green signal. At this step we don’t care about good code.
  3. Refactor your code from the previous step.

In the real world case scenario let’s implement TDD in Laravel with feature test.

Implementation

  • Create a project using command “composer create-project laravel/laravel exampleinsert”
  • Make few changes in ‘phpunit.xml by adding this two lines.

    Server name=”DB_CONNECTION” value=”sqlite”
    Server name=”DB_DATABASE” value=”:memory:”

  • Create a test case file by using “php artisan make:test ExampleInsertTest” which will create a ExampleInsertTest.php file inside tests/Feature/Folder and add below code.

    /** @test */

    Public function a_exampleinsert_can_be_added()
    {
        $this->withoutExceptionHandling();

        $response = $this->post(‘/exampleInsert’,[
            ‘title’ => ‘John Deo’,
            ‘address’ => ‘Nepal, Kathmandu’
        ]);

        $response->assertOk();
        $this->assertCount(1, Example::all());
         }
Enter fullscreen mode Exit fullscreen mode
  • Now run vendor/bin/phpunit from project root directory.

  • After running above command we get following error

Alt Text

  • ERRORS! With red color, it is because the route which we are trying to hit isn’t created yet and we get the 404 status code in response. So let’s create route in our web.php file.

    Route::post(‘/exampleInsert’,’ExampleInsertController@store’);

  • After creating route, run the test case and we get the error of ExampleInsertController does not exist.

Alt Text

  • Now create a controller with a store method and run the test case again and see the output console. We will get the following error. This error says that the Model of ExampleInsert is not available which we are calling in the assertCount method.

Alt Text

  • After creating the Model name with ExampleInsert yet we get another error output as follows with no such table.So we need to create a migration file.

Alt Text

  • Finally, after adding the migration file and store method with insert logic we get success green output.

Alt Text

  • Here, we developed the test case and then we moved on to the development of the insert feature of ExampleInsert.

At this point, we have finished our piece of implementation with TDD in mind, we have needed minimal code changes to pass all the rest cases and satisfy all requirements with refactoring, ensuring that we have great code quality.

Top comments (5)

Collapse
 
rolandhesz profile image
Roland Hesz • Edited

Just a brief addition:
With Laravel 7.10.4
$this->withoutExceptionHandling();
results in Error: Call to undefined method Tests\Unit\ControllerTest::withoutExceptionHandling()

If you try the below (apparently the way to do it in Laravel 6):

use Illuminate\Foundation\Testing\Concerns\InteractsWithExceptionHandling;

class testClass () {
use InteractsWithExceptionHandling;
public function testFunction() {
$this->withoutExceptionHandling();
....

you will get the error:
Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Debug\ExceptionHandler] is not instantiable.

Any idea how to get the real exceptions with Laravel 7?

Collapse
 
avsecdongol profile image
Abishek Dongol

I think cross test conflict may be the reason. Have you tried composer dump-autoload and still same error persist you can run the full test suite using the --process-isolation flag.

Collapse
 
rolandhesz profile image
Roland Hesz

Hi, in the meantime I found out the issue.
It's a really silly thing, but no tutorial ever explains that while we use PHPUnit for testing, the tests should NOT extend the PHPUnit TestCase class but the one provided by Laravel (\Tests\TestCase).

For some reason, that small, but extremely important piece of information is never mentioned in the articles, Youtube videos, etc. I have seen. That's something people are expected to know.

I didn't. That was the issue. ¯_(ツ)_/¯

Thread Thread
 
jonjieviduya profile image
Jonjie Viduya

@rolandhesz That actually works for me. Not sure why

Collapse
 
rolandhesz profile image
Roland Hesz

Still, I put your advice away in my "things to know" folder, probably will come handy at some point.