Part 1: Testing Model Relationships in Laravel — Schema Tests
Laravel makes TDD a lot more fun, it is such a joy to work with . The essence of the tests in this series is to ensure that your app model relationship is not missing a bolt. The code snippets will be available for easy reach at this Github project link.
Table of contents
To make this post more organized it will be split into three (3) parts.
- Part 1: Intro and Schema Tests ⏯
- Part 2: Testing Basic Model Relationships
- Part 3: Testing Polymorphic Relationships
Writing tests make your code rock solid, predictable and highly maintainable. I have written some terrible apps in the past and it can be unnerving when broken codes scream in — y o u r — face. Those years I had no clue about unit tests — fix one bug and another one surfaces, more annoying is that you don’t know when another will pop up. Errors are bound to occur if relevant models are missing or wrongly connected.
Unit and Feature Tests
Unit Tests are written for small pieces of a code base such as methods or attributes in a class. It usually deals with testing fine details at low level only. In our case we will be testing some methods on Laravel model classes, that is, model connections and relationships between different parts of an app.
Feature Tests is more encompassing as it sums up different part of a code and tests how well they inter-operate eg functional tests and integration tests on components, modules etc. We are not going into functional test in this post.
NB: it is assumed that you have a working knowledge of Laravel, therefore nitty gritty of installation, eloquent model setup and relationship are not discussed. However, test relevant topics will be discussed in depth.
To install a new laravel app :
-
laravel new modelRelTests
via installer -
composer create-project --prefer-dist laravel/laravel modelRelTests
via create-project
1. Set Up Test Suite
We are going to make use of an sqlite database and use it in memory for faster test runs. Go to phpunit.xml at the root directory of your application and add:
...
<server name=" **DB\_CONNECTION**" value=" **sqlite**"/>
<server name=" **DB\_DATABASE**" value=" **:memory:**"/>
2. Creating a Test Class
If we run vendor/bin/phpunit
at this point we get 2 tests for the dummy tests added by Laravel, one test each in the Unit and Feature test directories. Seeing the green is refreshing. You should delete the two files in preparation for our real tests.
By default, every new Laravel installation comes with a User model class (along with migration, controller etc) but not a test. We should create our own test file with the command:
php artisan make:test **UserTest --unit**
This creates a test file named UserTest.php within the tests/Unit directory in the root folder. The --unit flag
implies that we are creating a unit test and should add the generated file to the unit test directory. Without the --unit flag
we have a feature test and it gets added to tests/Feature directory.
3. Schema Test — My First Test in Every Model
Before visiting model relationship tests, my very first unit test in every Laravel model is the schema test. We can use this test to ensure that relevant table columns are not missing and also have the correct and expected names. All we need to do is this:
Take note of :
-
use Illuminate\Support\Facades\Schema;
namespace. -
RefreshDatabase
andWithFaker
traits. These two traits are important in most tests since you will most likely need a database of data to test your models and the faker library to populate a database with data to be tested upon.
If you want to be explicit about your schema checks you may check each individual field with:
-
...Schema:: **hasColumn** ('model', **'column'** ), 1);
instead of -
...Schema:: **hasColumns** ('model', **['column\_1', 'column\_2']**), 1);
which checks all columns existence at once.
I prefer **hasColumns()**
though, odd part is that you get a general error report and so don’t know the actual column affected unless you check thoroughly.
Run vendor/bin/phpunit
and see the result below 👇
This test failed, why? We have not set up or created our database yet. Set up your database configurations within the .env
file according to your machine. See the snippet below:
DB\_DATABASE=homestead
DB\_USERNAME=homestead
DB\_PASSWORD=secret
Then, create your database within your xampp, wamp or homestead and run:
$ php artisan migrate
Illuminate\Database\QueryException : SQLSTATE[42000]: If you encounter the Illuminate\Database\QueryException : SQLSTATE[42000]
error while running the migration like in image below
Go to App/Providers/AppServiceProvider.php and make the following updates. Within the boot method add Schema::defaultStringLength(191);
and add a the namespace like so use Illuminate\Support\Facades\Schema;
. See below 👇
Run vendor/bin/phpunit
again and you should get a success result as below:
Play around a little more by adding a new column into the test, do not add to the schema migration file initially, run the test to see results, failed? Make it pass. This particular test may look boring but from the ground up you can be sure your schema integrity is accounted for.
4. Creating Aliases for Your CLI Commands
Now that you are beginning to write tests there will be a lot of tests to run. It would make much sense to have shortcut form of some lengthy commands, the cli commands you use often. This will surely improve your productivity. Bash alias es to the rescue, it is a method of supplementing or overriding Bash commands with new ones eg allow users to customize their experience in a POSIX terminal.
-
alias name="command to shorten"
— creates a new alias ‘name’ NB: there are no spaces around the=
sign. -
alias
— displays all your set aliases directly in the CLI. - On a Windows PC, you may access and manage a list of your set aliases in the .bash_profile file at
C:\Users\yourUsername\.bash\_profile
. Here you can add, update or remove the entries.
I use the commands below most times in my tests:
-
pu
vendor/bin/phpunit
this runs all tests within the tests directory. -
puf
vendor/bin/phpunit --filter
this selectively runs test within all files and test methods whose name has the string (pattern). -
putu
vendor/bin/phpunit --testsuite Unit
this runs all tests within the Test\Units directory only. -
putf
vendor/bin/phpunit --testsuite Feature
this runs all tests within theTest\Features
directory only.
There are more others but these ones comes handy for me all the time.
Conclusion
I become paranoid if I write an app without some tests these days. I learnt the hard way and has since inculcate the habit of writing comprehensive tests for my apps — the predictability, ease of maintenance and confidence that accompanies a well tested codebase is indescribable. Kindly check the remaining two parts (see links below 👇) for they are the biggest deal.
- ⏯Part 1: Intro and Schema Tests
- 👉Part 2: Testing Basic Model Relationships
- 👉Part 3: Testing Polymorphic Relationships
Your comments are highly appreciated.
Top comments (5)
Thanks for breaking down the diff between unit and feature. I recently adopted TDD and i am now more confident in my code
Very good. Thanks for share.
My pleasure.
thanks, so good.
but I couldn't work with alias.
I'm confusing. maybe later I return here and try again.
thanks