Laravel Multi-Tenant Testing — Part 3
Here we go again! I will be going over feature and unit tests for a multi-tenant application. It took me a while to actually get this working since I couldn’t find much material online. So I will be passing this knowledge onto you!
This article is part of a series. Check out the rest below!
Part 0 — Laravel Multi-Tenant App Setup
Part 1 — Laravel Passport and Hyn\Tenancy
Part 2 — VueJS and Laravel API
Part 3 — Laravel Multi-Tenant Testing←You are Here
You can find the full code example below. Make sure to use the Main branch.
I spent a long time trying to piece this all together. Granted this was my first experience writing tests, but adding in API authentication and per-tenant testing scenarios didn’t help. To solve this particular issue, I used testing examples from the hyn\multi-tenant package and various examples from the internet.
I could refine the tests a little more by not creating a tenant for every single test. As of right now it significantly increases the testing run time (Takes about a 1:20min to run)
Edit: I took the time to optimize the tests! Check here for the result.
Setup
Here is my directory structure.
To explain:
- Endpoints — API Tests
- Feature — Any application features (i.e Auth)
- Scripts — OS level scripts to help with test cleanup
- Traits — Traits supporting with testing 3rd party packages
- Unit — Unit tests
We will need to update the phpunit.xml file to add some env variables and also add the Endpoints testing directory.
TicketFactory.php
Create a TicketFactory.php file in the database/factories directory. This will help with creating tickets for testing.
Database
Create a database named tenancy_test. This will be used when running tests and won’t affect your production database.
Supporting Traits
Here are some traits to help with testing Passport and Tenancy.
InteractsWithTenancy.php
I borrowed this from the hyn\multi-tenant
package and made some modifications to fit my needs.
The setupTenancy()
method is run during the test setup and creates the necessary tenant components. The setupHostnames()
and setupWebsites()
methods actually persistent the models to the database. And finally the activateTenant()
method will set the current tenant as active.
InteractsWithPassport.php
I use this trait to override the Laravel API methods to attach the correct authentication headers. I call the createUserWithToken()
method from within the tests.
Base Test Class
I created a base class for tenant tests that allows opportunities for the tests to create tenants.
Unit Tests
The only Unit test I have is one that tests the Middleware tenant identification. It just makes sure the tenant connection is set.
Feature Tests
Here I test the Laravel Auth functionality. These will extend the TenantTestCase
Class
LoginTest.php
PasswordResetTest.php
RegistrationTest.php
Endpoint Tests
These tests will make sure the API is working like it should. It will utilize the InteractsWithPassport
trait to simulate an authenticated user. The tests will also test if json values are returned.
TicketAPITest.php
Run Those Tests!
Every piece of code written comes down to the point. It is time to run the tests and pray for the green check marks. Run the command below
./vendor/bin/phpunit --testdox
And you should be something like this
If you are using mysql, you might get an error like:
PDOException: SQLSTATE[HY000]: General error: 1819 Your password does not satisfy the current policy requirements
You can set the password policy to LOW using this command
SET GLOBAL validate_password_policy=LOW;
Also note there is a script that will delete all of the tenant databases. I had a few hundred created by the time I actually got this working. I borrowed this one from the hyn\multi-tenant
package also.
Just replace $password with your mysql root password (Assuming you are using MySQL)
Conclusion
That is it for the testing! Hopefully you were able to get the green check marks while testing in the demo or your own app. I might expand the demo a little more to include relations and more complex operations. I am still thinking about doing a graphql version of this also.
Thanks for reading!