CRUD Unit Testing in Laravel 5
Recently, I started an open source e-commerce application called LARACOM made with Laravel together with Rob Gloudemans’ shopping cart and other related e-commerce packages.
When starting this project, I have to think of the long term so never in my mind that I will not use the TDD (test driven development) approach, it is a MUST. So in doing it, I need my tests to be separated in two different groups, one for Unit testing and one for Feature testing.
Unit testing is testing your classes — Models, Repositories etc. while Feature testing is testing your code if it hits the controllers and asserting the actions it will throw, say a redirect, returns a view or redirect with error flash messages.
Enough with the introduction. If you haven’t tried TDD before, I have written a basic TDD approach in here. I will not talk about the basic already since there is already an article for it.
What I will do today is doing a Carousel for my e-commerce project.
Edit: Hey! I created a base repository package you can use for your next project :)
Part I: Positive Unit Testing
Let’s start with the CREATE test.
Create the file /tests/Unit/Carousels/CarouselUnitTest.php
What are we trying to do in this file:
- See if our repository class can take this data parameters and actually create the carousel in the database
- See if after creating the carousel, the response have the same values as the parameters we feed it in
Now, in your terminal, run vendor/bin/phpunit
(you must be in the root directory of your application)
Is it an error? Yes! It should be since we have not created the files it is trying to use.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.E 1 / 1 (100%)Time: 700 ms, Memory: 26.00MBThere was 1 error:1) Tests\Unit\Carousels\CarouselUnitTest::it_can_create_a_carousel
Error: Class 'Tests\Unit\Carousels\CarouselRepository' not found
So we need to solve the error by creating the CarouselRepository
file.
Let’s create it in app/Shop/Carousels/Repositories/CarouselRepository.php
Obviously, you cannot find the
Shop
andCarousels
folder in the default Laravel installation. You can do your own way on how to structure your folder but I’d love to do it this way. You just have to namespace it properly.We also created a specific error exception file here.
Create it in,
app/Shop/Carousels/Exceptions
folder otherwise it will throw an error.
In this repository, we use dependency injection. We inject the class to the repository so it can use it. The Carousel
model would throw also an error here since it is not yet existing.
We create it first: app/Shop/Carousels/Carousel.php
After the repository is created, import it to our test file like so:
and run again vendor/bin/phpunit
Error? Yes? Yup. You guessed it right.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.E 1 / 1 (100%)Time: 898 ms, Memory: 26.00MBThere was 1 error:1) Tests\Unit\Carousels\CarouselUnitTest::it_can_create_a_carousel
App\Shop\Carousels\Exceptions\CreateCarouselErrorException: PDOException: SQLSTATE[HY000]: General error: 1 no such table: carousels in /Users/jsd/Code/shop/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:77
It is trying to insert the data into the database but the table is not yet created. What do we need now? Migration file.
In your terminal, run:
php artisan make:migration create_carousels_table --create=carousels
Once it is created, open it and configure the columns you need.
The link is nullable but definitely the title and the image src are required.
Once this is done, run vendor/bin/phpunit
again.
Well done mate, well done.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.. 1 / 1 (100%)Time: 696 ms, Memory: 26.00MBOK (1 test, 6 assertions)
You already have one test passing. So, as long as this test passes whenever you run vendor/bin/phpunit
, you can be confident that the application can create a carousel slider always.
Show the carousel after it is created
Now, lets try to test the READ.
See if we can see the carousel once it is created.
Again, run the test. Every test we newly created, we always expect a failed test. Why? It is because we have not yet done any implementation. If we get a success after we create the test, then you are doing it WRONG.
Note: Every test we create should be at the top as we want it to be run first
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.E 1 / 1 (100%)Time: 688 ms, Memory: 26.00MBThere was 1 error:1) Tests\Unit\Carousels\CarouselUnitTest::it_can_show_the_carousel
InvalidArgumentException: Unable to locate factory with name [default] [App\Shop\Carousels\Carousel].
With this error, it is looking for the model factory for the Carousel.
So, we need to create it in: database/factories/CarouselModelFactory.php
Run the phpunit again.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.E 1 / 1 (100%)Time: 708 ms, Memory: 26.00MBThere was 1 error:1) Tests\Unit\Carousels\CarouselUnitTest::it_can_show_the_carousel
Error: Call to undefined method App\Shop\Carousels\Repositories\CarouselRepository::findCarousel()
Yup. The factory error is now gone. Meaning it can now persist in the database. Others, they want persistence to be separate so you can put in your test instead of :
$carousel = factory(Carousel::class)->create();
then
$carousel = factory(Carousel::class)->make();
But we still have an error, it can’t find the findCarousel()
method in the repository class. We create that method.
Self explanatory file. Run the phpunit and see the output.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.. 1 / 1 (100%)Time: 932 ms, Memory: 26.00MBOK (1 test, 6 assertions)
Pat on the back mate. Good job.
Update Carousel test
Now, see if we can UPDATE the carousel
In here, we use again the model factory to create the carousel the we pass $data
parameters to update the created carousel and assert if we can get the same values in the update $data
parameters.
This will fail as you know because the updateCarousel
method is not yet created so we create it.
Once this method is created, run phpunit again.
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.. 1 / 1 (100%)Time: 932 ms, Memory: 26.00MBOK (1 test, 6 assertions)
It just work out of the box!
Finally, we test the delete
Create a test if we can delete the carousel we created.
Then create the deleteCarousel()
method
When this method is hit, it should return a boolean
response. True
if successfully delete else null.
Then run phpunit again.
➜ git: phpunit --filter=CarouselUnitTest::it_can_delete_the_carousel
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.. 1 / 1 (100%)Time: 916 ms, Memory: 26.00MBOK (1 test, 1 assertion)
WOW. You have made it here! CONGRATULATIONS!
If you want to see tests live in action, heads up to my Unit Tests here
###
As a PART II, let us do NEGATIVE testing!
/jsd