under Laravel
Learn how to add Stripe's Checkout and Customer Portal products to your Laravel application using Laravel Cashier.
under Laravel
Follow along as we build a drop down component with Caleb Porzio's Laravel Livewire.
under Laravel
Laravel's default setup prevents Cloudflare from caching HTML responses.
under Laravel
Calling seeders from migrations allows you to ensure data referenced by code always exists in your database.
under Laravel
If you're using ElastiCache with the Encryption In-Transit setting turned on, you'll need to tweak your REDIS_HOST environment variable when connecting with Laravel
under Laravel
Laravel defaults to using an in-memory SQLite database for testing. Using this type of database has some advantages over a traditional MySQL database such as less configuration, your tests "just work", they run faster than a traditional database, etc. However, there are also some disadvantages as well:
RefreshDatabase
trait that comes out of the box with Laravel, a SQLite test suite can actually be much slower than MySQLI'm working on a project where we just transitioned from an in-memory SQLite test suite to a MySQL test suite. At the time of the transition, we had 52 migrations in the project. A full test suite (426 tests) with the in-memory driver took between 1.5 minutes to 3 minutes, depending on the machine running the tests.
After the transition, the full test suite (with more tests too, up to 495 at the time of writing this post), now runs between ~14 seconds to ~20 seconds, depending on the machine running the tests.
1PHPUnit 8.5.3 by Sebastian Bergmann and contributors. 2 3............................................................... 63 / 495 ( 12%) 4............................................................... 126 / 495 ( 25%) 5............................................................... 189 / 495 ( 38%) 6............................................................... 252 / 495 ( 50%) 7............................................................... 315 / 495 ( 63%) 8............................................................... 378 / 495 ( 76%) 9............................................................... 441 / 495 ( 89%)10...................................................... 495 / 495 (100%)11 12Time: 14.49 seconds, Memory: 88.50 MB
The key is in the RefreshDatabase
trait. When using the trait with an in-memory database, Laravel will re-migrate the database with each test run. So as an example, if your migrations take 1.5 seconds to run, each test using the RefreshDatabase
trait in your suite will take at least 1.5 seconds. Some basic math for a test suite with 400 tests:
400 * 1.5 = 600 seconds = 10 minutes!!
Ouch.
As fast as in-memory is, the root problem is that you're running migrations over and over again. This is where RefreshDatabase
shines.
Instead of running your migrations from scratch for each test, the trait keeps track once your migrations have been run for the first time. From that point forward it uses database transactions to rollback the database after each test, which resets the database to the state it was in at the beginning of the test (migrated but with no data).
There is a small caveat that you need to be aware of. When the database rolls back the transaction, any auto-incremented keys are not rolled back. Databases do this to prevent primary key collisions when using transactions.
So if you're asserting that a database includes a hard coded ID, you'll need to update your assertions because you can no longer guarantee that auto-increment fields will start at 1.
We made a simple helper class to use for our tests:
1<?php 2 3namespace Tests\Helpers; 4 5use Illuminate\Support\Facades\DB; 6 7class AutoIncrement 8{ 9 public static function nextId(string $modelClass)10 {11 $model = resolve($modelClass);12 13 if (!$model->getIncrementing()) {14 throw new \Exception("{$model->getTable()} does not use an incrementing key.");15 }16 17 $result = DB::select("SHOW TABLE STATUS LIKE '{$model->getTable()}'");18 return $result[0]->Auto_increment;19 }20}
Usage of this class looks like this:
1<?php 2 3// Before we make our Post, determine the next ID 4$nextId = AutoIncrement::nextId(Post::class); 5 6// Call the endpoint to create a new Post here.. 7 8// Assert the Post was created with the correct ID 9$this->assertDatabaseHas('posts', [10 'id' => $nextId,11 'title' => 'New Post',12]);
There's two steps to switching to using MySQL for your tests:
phpunit.xml
to use the MySQL connection and new database:1<server name="DB_CONNECTION" value="mysql"/>2<server name="DB_DATABASE" value="your_test_database"/>
Apart from those two changes, you'll have to update any tests that assert a hard coded auto-incrementing ID as described above!
Hopefully you found this article useful! If you did, share it on Twitter!
Found an issue with the article? Submit your edits against the repository.