Published Articles

Enter a search term to find articles.

Once in a while when diving deep into the Laravel codebase, I find that there are hidden gems that I think would really be helpful to developers. In this post, I'll shed light on one of those hidden gems. I'm talking about the `AssertableJsonString::class` that is new since Laravel version 8.x. Let's go! Typically when testing json responses, we have the ability to chain json assertions from the response like so: ``` public function test_json_response() { $response = $this->get(route('some.json.endpoint')); $response->assertJson(['foo' => 'bar']) ->assertJsonMissing(['bar' => 'baz']); } ``` Thanks to the fact that response is an instance of `Illuminate\Testing\TestResponse::class` which holds these convenient json assertion methods. However, there are **cases that we need to make assertions against json data that isn't coming from a json endpoint**. One example is reading a json file: ``` public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // This won't work since $data is just a plain array and // not an instance of Illuminate\Testing\TestResponse $data->assertJsonFragment(["vue" => "^3.2.31"]); } ``` Our typical assertions will not work on this example since `$data` is just a plain array. Before Laravel 8.x my workaround for this is to either pull in a third-party package specifically for json assertions, or wrap the json data with `Illuminate\Testing\TestResponse::class` like so: ``` use Illuminate\Http\Response; use Illuminate\Testing\TestResponse; ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // Wrap the data with Illuminate\Testing\TestResponse class // so that you can perform your typical json assertions... $response = TestResponse::fromBaseResponse(new Response($data)); $response->assertJsonFragment(["vue" => "^3.2.31"]); } ``` This approach works, but feels kind of hacky. For one, the data is not really an HTTP response so why does it have to go through these response classes? Thankfully, Laravel 8.x extracted these json assertions into a class called `AssertableJsonString::class`! Now we can just wrap our json data with this class and perform our assertions like so: ``` use Illuminate\Testing\AssertableJsonString ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); $data = new AssertableJsonString($data); $data->assertFragment(["vue" => "^3.2.31"]) ->assertMissing(["php" => "^8.1" ]); } ``` Enjoy coding!


Today at work I implemented Parallel Testing to one of our projects. Perfect timing as we also just recently upgraded to Laravel 8 (coming from Laravel 7). I was certain that Parallel Testing is available in Laravel 8 as I followed the development of this feature since the beginning of this year. However, after installing `brianium/paratest` package to our app and running the command `php artisan test --parallel` I encountered this error: ![Parallel Testing Error: Requires Laravel 9]( This got me confused, so I checked the Laravel 8 documentation for Parallel Testing and it was there. []( However, this doesn't solve the issue as nothing in the documentation mentions this error. So I decided to dig a bit further. And as it turns out, Parallel Testing was originally created for Laravel 9 and then backported to Laravel 8. Due to this, earlier versions of `nunomaduro/collision` package (5.0, 5.1, 5.2 - which what I was using at the time) had a restriction to only use `--parallel` option on Laravel 9, but version 5.3 removed that restriction. [This link shows comparison of collison version 5.2 to 5.3.]( Checkout line 74 of `src/Adapters/Laravel/Commands/TestCommand.php` file for the removal of restriction. So I promptly updated `nunomaduro/collision` to `^5.3` on my project and fixed the issue. While at it I also took the liberty of creating a [pull request to laravel documentation]( so that this issue is properly documented and other developers don't waste their time in the future. Peace!


Laravel team has recently announced that they are moving from a 6 month major release cycle to 12 month major release cycle. This is due to many users feeling that versions were being released frequently since adopting the *"semantic versioning"* standard even though the cycle speed stayed the same. And because of this decision, the team decided to backport parallel testing to Laravel 8, which was supposed to be a major feature of the Laravel 9 release. Developers like us can now enjoy the benefits of faster running tests as of Laravel 8.25! We can read the full announcement here: [Laravel: New Release Schedule]( Here's a preview of tests in one of my major projects before and after implementing parallel test. ![]( As you can see, that's about 50% increase in time it took to complete the entire test suite and more than 80% reduction on memory usage. I don't know about you but that is insane! You can read full details of the new feature here [Laravel: Parallel Testing Is Now Available](


TIL that you do not need the full class name (or the full method name) when filtering tests to run. I was working on some documentations writing today when I ran my tests with a mistyped `User` filter and to my surprise, it loaded and ran all tests with the word `User` on class names and methods. Below is how it looked like after I ran the command (some tests have been redacted).. ``` ➜ p2p git:(develop) ./vendor/bin/phpunit --filter=User PHPUnit 8.5.8 by Sebastian Bergmann and contributors. Batch Import Job (Tests\Unit\BatchImportJob) ✔ It belongs to a user Company (Tests\Unit\Company) ✔ It belongs to many users Contract (Tests\Unit\Contract) ✔ It belongs to many users Media Security Check (Tests\Unit\MediaSecurityCheck) ✔ It belongs to a user User Setting (Tests\Unit\UserSetting) ✔ It belongs to a supervisor ✔ It belongs to a user ✔ It belongs to a delegate Companies Deactivation (Tests\Feature\Admin\CompaniesDeactivation) ✔ Deactivating a company also deactivates its users User Change Password (Tests\Feature\Admin\UserChangePassword) ✔ Can change password of a user ✔ Can resend email password reset to user User Contracts (Tests\Feature\Admin\UserContracts) ✔ Can attach a contract to a user User Deactivations (Tests\Feature\Admin\UserDeactivations) ✔ Can deactivate a user ✔ Deactivated users cannot login ✔ Can reactivate a user User Export (Tests\Feature\Admin\UserExport) ✔ Can export users User Settings (Tests\Feature\Admin\UserSettings) ✔ Can update user settings Users (Tests\Feature\Client\Users) ✔ Can list all users ✔ Can get user details ✔ Can only get details of users in the same company User Filters (Tests\Feature\Filters\UserFilters) ✔ It can load user companies ✔ It can be filtered by company association User Picklists (Tests\Feature\Supplier\UserPicklists) ✔ Can update picklist Users (Tests\Feature\Supplier\Users) ✔ Can list all users [Some tests have been redacted] Time: 44.64 seconds, Memory: 86.50 MB OK (83 tests, 204 assertions) ```


New to Laravel 8 is the ability to quite literally travel in time using the Wormhole class. Although this class was only recently implemented on version 8, the time travelling concept is already apparent using the `Carbon::setTestNow()` which is a feature available in `Carbon\Carbon` package, and is in fact behind this class. Personally, I was able to utilize this feature when I was working on a Loans Management app, which made simulating past and future scenarios (i.e: loans terms and payments) in my tests quite a breeze. Let's take a look at how we can make time travelling work in Laravel 8. ## The `InteractsWithTime` trait In Laravel 8, a new trait for testing has been created as a sort of wrapper for the Wormhole class. The InteractsWithTime trait is within `Illuminate\Foundation\Testing\Concerns` namespace (be careful when importing and not to confuse it with other traits with the same name but in a different namespace). So we can just use it in our test classes like so.. namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\Concerns\InteractsWithTime; class TimeTravellingTest extends TestCase { use InteractsWithTime; } Once we've used it on our test class we then gain access to various utility methods.. // Travel 8 days in the future $this->travel(8)->days(); // Carbon::now() or now() will result to the current date + 8 days // Travel 10 minutes in the future $this->travel(10)->minutes(); // Carbon::now() or now() will result to the current time + 8 minutes // Travel 5 months in the past $this->travel(-5)->months(); // Carbon::now() or now() will result to the current date - 5 months We can also travel to a specific date.. $this->travelTo('2019-02-14 12:00:00'); // Carbon::now() or now() will result to '2019-02-14 12:00:00' Just make sure to **ALWAYS GO BACK TO THE PRESENT** after doing time travelling to not get stuck in the past or present. $this->travelBack(); // Carbon::now() or now() will result to the current date That means we can also time travel, execute a callback and return to the current date consecutively.. // Option 1.. $this->travel(8)->days(function () { dd(now()); // This will result to the current date + 8 days // After executing this callback, now() will automatically go back to the current date }); // Option 2.. $this->travelTo('2019-02-14 12:00:00', function () { // Carbon::now() or now() will result to '2019-02-14 12:00:00' echo 'Happy Valentines Day!'; // After executing this callback, Carbon::now() or now() // will automatically go back to the current date }); ## The `Wormhole::class` The `Wormhole::class` is namespaced within `Illuminate\Foundation\Testing`. Looking at it, its just a single file class that accepts an integer value. This means we can just new it up like a traditional php class, give it a value and call the methods within it, like so.. $wormhole = new Wormhole(8); $wormhole->days(); // Carbon::now() or now() will result to the current date + 8 days Wormhole::back(); // Go back to the present time.. And again we can execute a callback within the Wormhole method.. $wormhole = new Wormhole(8); $wormhole->days(function () { dd(now()); // This will result to the current date + 8 days // After executing this callback, now() will automatically go back to the current date }); This is useful if you wish to do time travelling outside your tests and don't want to use the trait. And there you have it, time travelling in Laravel is now online. Hope you learned something from this article. Cheers!


Some small and practical testing tips from yours truly. 1. If your tests are not hitting the database, remove the `use RefreshDatabase` statement. This will greatly speed up your test as it won't run migrations. ``` class ExampleTest { use RefreshDatabase; ... } ``` 2. If you want to see details when you run your tests but don't want to use 3rd party printer package, you can utilize the `--testdox` flag in PHPUnit. This flag will show which test class and method is currently running. ``` phpunit --testdox ``` 3. Getting overwhelmed with too many errors when you run a group of tests or your entire test suite? Use the `--stop-on-failure` flag in PHPUnit. This will cause the test to stop on first occurence of failure. ``` phpunit --stop-on-failure ``` Happy testing!

Marvin Quezon · Copyright © 2024 · Privacy · Sitemap