Test Utilities for Laravel

Utilities and helpers for testing Laravel based applications.

composer require --dev savvywombat/laravel-test-utils
Latest Version on Packagist Supported PHP Version MIT License

Database casting helpers

use SavvyWombat\LaravelTestUtils\DBCast

DBCast::toJson

Use this casting helper when making assertions against the database (such as with assertDatabaseHas or assertDatabaseMissing) to cast an array or json string to an SQL JSON datatype.

$this->assertDatabaseHas('vehicles', [
    'id' => 1,
    'manufacturer' => 'Toyford',
    'model' => 'Llama',
    'attributes' => DBCast::toJson([
        'color' => 'indigo green',
        'engine' => '2 litres 4-cylinder',
        'gearbox' => '6-speed manual',
        'doors' => '5',
    ]),
]);

DBCast::toTimestamp

Use this casting helper to assert DateTime or Carbon instances against SQL timestamps in the database.

$trip = Trip::factory()->create();

Carbon::setTestNow("2020-10-20 10:15:43");

$response = $this->get('/start-trip');

$response->assertStatus(200)
    ->assertSee('Trip started');

$this->assertDatabaseHas('trips', [
    'id' => $trip->id,
    'trip_started_at' => DBCast::toTimestamp(Carbon::now()),
]);

Carbon::setTestNow();

Mock guzzle

This trait assumes that you are using Laravel's dependency injection to inject a Guzzle client into your code. Using this trait in a test case will inject a mock client in place of your application's default client, allowing you to test your code's reaction to predictable responses without actually connecting to the remote API.

namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Illuminate\Http\Request;

class ContactController extends Controller
{
    public function send(Request $request, Client $client)
    {
        // Validate ReCaptcha as part of a contact form send request
        $response = $this->client->post(config('recaptcha.url'), [
            'query' => [
                'secret' => config('recaptcha.secret'),
                'response' => $request->input(['g-recaptcha-token'], ''),
            ]
        ]);

        if (json_decode($response->getBody())->success) {
            redirect()->route('contact::success');
        } else {
            redirect()->route('contact::form')->withErrors(['g-recaptcha-token' => 'Please confirm you are a human!']);
    }
}
namespace Tests\Feature;

use GuzzleHttp\Psr7\Response;
use SavvyWombat\LaravelTestUtils\MocksGuzzle;
use Tests\TestCase;

class MyTest extends TestCase
{
    use MocksGuzzle;

    /** @test */
    public function it_reacts_appropriately_to_recaptcha_success()
    {
        $this->guzzle() // this is guzzle's MockHandler class
            ->append(new Response(200, [], json_encode(['success' => 'true'])));

        $this->post('/some-url', ['message' => 'some message', 'g-recaptcha-token' => 'blah'])
            ->assertStatus(302)
            ->assertSessionHasNoErrors()
            ->assertRedirect('/success');
    }

    /** @test */
    public function it_errors_on_recaptcha_failure()
    {
        $this->guzzle() // this is guzzle's MockHandler class
            ->append(new Response(200, [], json_encode(['success' => 'false'])));

        $this->post('/some-url', ['message' => 'some message', 'g-recaptcha-token' => 'blah'])
            ->assertStatus(302)
            ->assertSessionHasErrors('g-recaptcha-token')
            ->assertRedirect('/some-url');
    }
}

If your controller or other code under test needs to make more than one request to an API, you can chain responses to the guzzle handler:

$this->guzzle()
    ->append(new Response(200, [], json_encode(['invoices' => ['id' => '1245', 'id' => '1247']])))
    ->append(new Response(404));

Caveats

Currently, the guzzle mock doesn't assert or check anything regarding what requests you are making - it's just a simple way to test your code's reaction to a particular response without having to actually transmit a request to the remote API.