Laravel Common PHP Unit Testing CRUD made easy

If you’re doing PHP unit test in Laravel, this code snippets might be useful for you.

Basically, the most common test we made on every features is CRUD. So I made this code to use it on every feature.

Let’s get started.

First, I created a utility function for factory just like Jeffrey Way did on his tutorial. :)

<?php/* tests\utilities\functions.php */
function create($class, $attributes = [], $times = null)
{
return factory($class, $times)->create($attributes);
}
function make($class, $attributes = [], $times = null)
{
return factory($class, $times)->make($attributes);
}
function raw($class, $attributes = [], $times = null)
{
return factory($class, $times)->raw($attributes);
}

Then add the file path in composer.json autoload-dev.

"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
},
"files": [
"tests/utilities/functions.php"
]

}

So on TestCase.php, I added the function that we will be using on every feature test.

Image for post
Image for post

You can get it here: https://gist.github.com/wanmigs/8ed74633ddff91062f51e8b9aa2778a9

So now we have added the code on TestCase.php, let’s now create a test. To make it easier let’s create a model with migrations, controller and factory.

php artisan make:model Demo -a

This will generate

Model created successfully.
Factory created successfully.
Created Migration: 2019_02_24_181538_create_demos_table

So on migration, let’s have this as example.

Schema::create('demos', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('address')->nullable();
$table->timestamps();
});

Then, on you factory DemoFactory.php

$factory->define(App\Demo::class, function (Faker $faker) {
return [
'name' => $faker->name,
'address' => $faker->streetAddress
];
});

DemoController.php

public function store(Request $request)
{
request()->validate(['name' => 'required']);
$demo = Demo::create(request()->all()); return response($demo, 201);
}
public function update(Request $request, Demo $demo)
{
request()->validate([
'name' => 'required',
]);
$demo = tap($demo)->update(request()->all()); return response()->json($demo);
}
public function destroy(Demo $demo)
{
$demo->delete();
return response([], 204);
}
public function destroyAll()
{
Demo::whereIn('id', request('ids'))->delete();
return response([], 204);
}

Now we have set the controller. We’re going add it in our route

Route::resource('demo', 'DemoController');
Route::delete('demo', 'DemoController@destroyAll')
->name('demo.destroyAll');

You can check resource controller on laravel documentation https://laravel.com/docs/5.7/controllers#resource-controllers

Let’s get real on unit testing now.

Make sure to add this on phpunit.xml to have our test data stored in memory only not in your actual database.

<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

Create a feature test now. First, we’re going to make a Create test

php artisan make:test CreateDemoTest

CreateDemoTest.php

Image for post
Image for post

As you can see on public function setUp, we set the base route and base model that we defined in TestCase.php. Also the create method in TestCase.php returns json reponse so we can able to assert json and status.

// This are the parameters the function have$this->create($attribute = [], $model = '', $route ='');//Also returns json response so you can assert json or status// return json response so you can assert json or status$this->create()->assertJsonFragments();// if you have custom route$this->create([], 'App\Demo', custom.route');//you can have the first parameter empty if base model is set
$this->create([], null, 'custom.route');

Run the test.

$ vendor/bin/phpunit --filter CreateDemoTest
PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
... 3 / 3 (100%)Time: 1.05 seconds, Memory: 26.00 MBOK (3 tests, 5 assertions)

Let’s have UpdateDemoTest now

php artisan make:test UpdateDemoTest
Image for post
Image for post
// This are the parameters the function have
$this->update($attribute = [], $model = '', $route ='');
//Also returns json response so you can assert json or status

Run the test

$ vendor/bin/phpunit --filter UpdateDemoTest
PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)Time: 905 ms, Memory: 26.00 MBOK (2 tests, 3 assertions)

Lastly, DeleteDemoTest

php artisan make:test DeleteDemoTest
Image for post
Image for post
$this->destroy($model = '', $route ='');// return json response so you can assert json or status$this->destroy()->assertJsonFragments();// if you have custom route$this->destroy('App\Demo', custom.route');//you can have the first parameter empty if base model is set
$this->destroy(null, 'custom.route');

Run the test

$ vendor/bin/phpunit --filter DeleteDemoTest
PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
... 3 / 3 (100%)Time: 1.02 seconds, Memory: 26.00 MBOK (3 tests, 4 assertions)

Conclusion

Thanks for reading!

Written by

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