About Test Driven Laravel
This repository is not the course! The course was written by Adam Wathan and can be purchased from him here: https://testdrivenlaravel.com/
If you have not purchased the course, this repository will be of little to no value for you. This repository is simply where I'm working through the course content and documenting a few issues I encounter along the way.
First and foremost is the issue of doing the course using Laravel 7.x. It was originally created for Laravel 5.3. I'm running through the course using Laravel 7.x (version 7.24 at the time of this writing) and have run into a few problems simply getting started. The course itself covers upgrading to Laravel 5.4, then 5.5, but this seemed a silly exercise to me since I'm creating all my new projects in Laravel 7.x and wanted to put my new knowledge directly to work. And so here's what you need to do to take the course in Laravel 7.x.
browser-kit-testing
composer package
Add the The browser-kit-testing package allows you to use older Laravel 5.3 style testing functionality. Much of that functionality was migrated to other packages in later releases. Specifically "Frontend" tests were largely migrated to Laravel Dusk which runs a full-on headless browser, but Dusk is actually much much slower for running tests that only need to make simple HTTP requests. At least initially we won't need to be testing javascript or intricate frontend layout stuff, so it makes perfect sense to keep using the old style tests. In newer Laravel releases, the same sorts of simple HTTP tests are still possible without using Dusk, but the syntax is slightly different so, for compatibility with this course, just install browser-kit-testing and live with knowing you might want to use slightly different testing libs with slightly different syntax in the future.
composer require laravel/browser-kit-testing --dev
TestCase
class
Modify your application's base Tweak /tests/TestCase.php
to extend Laravel\BrowserKitTesting\TestCase
instead of Illuminate\Foundation\Testing\TestCase
and add the $base_url property. In the end, your TestCase.php should look like this:
<?php
namespace Tests;
// Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Laravel\BrowserKitTesting\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
public $baseUrl = 'http://localhost';
// ...
}
Now run composer dump-autoload
just to be sure.
Now run artisan test
and your tests should start running properly. I mean, they will fail properly and you can get down to business writing the code to make them pass. Happy coding!
NOTE: There is more extensive documentation regarding some alternative changes you can make to your tests to keep the original base TestsCase class unmodified. This way you could run newer style tests alongside the older style tests. You can see that documentation here: https://laravel.com/docs/5.4/upgrade
However, since it's a pain to go there and scroll down 3/4 of the page to find it among the other documentation on that page, here is the relevant excerpt:
Running Laravel 5.3 & 5.4 Tests In A Single Application
First install the
laravel/browser-kit-testing
package:composer require --dev laravel/browser-kit-testing "1.*"Once the package has been installed, create a copy of your
tests/TestCase.php
file and save it to yourtests
directory asBrowserKitTestCase.php
. Then, modify the file to extend theLaravel\BrowserKitTesting\TestCase
class. Once you have done this, you should have two base test classes in yourtests
directory:TestCase.php
andBrowserKitTestCase.php
. In order for yourBrowserKitTestCase
class to be properly loaded, you may need to add it to yourcomposer.json
file:"autoload-dev": { "psr-4": { "Tests\\": "tests/" } }Tests written on Laravel 5.3 will extend the
BrowserKitTestCase
class while any new tests that use the Laravel 5.4 testing layer will extend theTestCase
class. YourBrowserKitTestCase
class should look like the following:<?php use Illuminate\Contracts\Console\Kernel; use Laravel\BrowserKitTesting\TestCase as BaseTestCase; abstract class BrowserKitTestCase extends BaseTestCase { /** * The base URL of the application. * * @var string */ public $baseUrl = 'http://localhost'; /** * Creates the application. * * @return \Illuminate\Foundation\Application */ public function createApplication() { $app = require __DIR__.'/../bootstrap/app.php'; $app->make(Kernel::class)->bootstrap(); return $app; } }Once you have created this class, make sure to update all of your tests to extend your new
BrowserKitTestCase
class. This will allow all of your tests written on Laravel 5.3 to continue running on Laravel 5.4. If you choose, you can slowly begin to port them over to the new Laravel 5.4 test syntax or Laravel Dusk.
Getting Started With Validation
In this section of the course, Adam shows how you can create a function to easily diable Laravel's built-in exception handling so you can see the real exceptions in your tests. To do this he creates a custom function in the TestCase class, but it will not quite work in Laravel 7. You need to pass a Throwable instead of an Exception to the report
and render
functions. Here's a Laravel 7 version of the method...
/**
* This overrides Laravel's automatic exception handling
*/
protected function disableExceptionHandling() {
$this->app->instance(ExceptionHandler::class, new class extends Handler{
public function __construct(){}
public function report(Throwable $e){}
public function render($request, Throwable $e){
throw $e;
}
});
}
In the email_is_required_to_purchase_tickets test...
...instead of the using the assertArrayHasKey function to make sure the response contains an email validation error message, use the assertJsonValidationErrors on the $this->response
object....
/** @test */
public function email_is_required_to_purchase_tickets(){
// Arrange
$paymentGateway = new FakePaymentGateway;
// Bind the FakePaymentGateway class to the PaymentGateway interface so we can type hint the
// interface in the controller methods.
$this->app->instance(PaymentGateway::class, $paymentGateway);
// Create a concert
$concert = factory(Concert::class)->create();
// Act
// Purchase concert tickets
$this->json('POST', "/concerts/{$concert->id}/orders", [
'ticket_quantity' => 3,
'payment_token' => $paymentGateway->getValidTestToken(),
]);
// Laravel uses response code 422 for validation error responses
$this->assertResponseStatus(422);
// Assert that there are json validation errors
$this->response->assertJsonValidationErrors('email');
}