June 5, 2020 by Glenn

Using Laravel Assertions in Codeception

If you're using Codeception as a testing framework for your Laravel app, you may find yourself wanting to use some of the familiar Laravel testing assertions that you can find in Illuminate\Testing\TestResponse. In this article, we'll dive into how to do exactly that.

Step 1) Getting Setup

I assume you're already using both Laravel and Codeception, so I won't go down the road of helping you set those up. However to make sure we're on the same page; you need to tell Codeception you're using Laravel.

Install the module-laravel5 package and enable the module within your test suite's yaml file. Let's also add a useLaravel helper method to make interacting with this module easier.

composer require codeception/module-laravel5 --dev
# tests/functional.suite.yml
actor: FunctionalTester
modules:
    enabled:
        # add a framework module here
        - Laravel5:
              environment_file: .env.testing
        - \Helper\Functional
    step_decorators: ~
# tests/_support/Helper/Functional.php
<?php
namespace Helper;
class Functional extends \Codeception\Module
{
    public function useLaravel()
    {
        return $this->getModule('Laravel5');
    }
}

Step 2) Writing Our Test

Let's write a test that asserts we can succesfully make a get request to /api/users and verify the response. First let's head over to routes/api.php and add the following:

# routes/api.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/users', function () {
    return [
        'name' => 'Jeff Lebowski',
    ];
});

If we were to use Laravel's built in test suite, we'd probably write the following feature test:

# tests/Feature/UserApiTest.php
<?php
namespace Tests\Feature;
class UserApiTest extends Tests\TestCase
{
    /** @test */
    public function can_get_users()
    {
        $this->getJson('/api/users')
            ->assertSuccessful()
            ->assertJson(['name' => 'Jeff Lebowski']);
    }
}

How can we replicate this in Codeception? The key is being able to populate an instance of Illuminate\Testing\TestResponse. There are a few steps we need to take to get there.

First we'll get an instance of Symfony\Component\BrowserKit\Response by using Codeception's getInternalResponse method.

We'll use that to create an instance of Illuminate\Http\Response.

Finally we'll pass that into Illuminate\Testing\TestResponse.

# tests/_support/Helper/Functional.php
<?php
namespace Helper;
use Illuminate\Testing\TestResponse;
use Illuminate\Http\Response as IlluminateResponse;
class Functional extends \Codeception\Module
{
    /** @var TestResponse */
    private $laravelResponse;

    public function useLaravel()
    {
        return $this->getModule('Laravel5');
    }

    public function getLaravelResponse()
    {
        if (! $this->laravelResponse) {
            // returns \Symfony\Component\BrowserKit\Response
            $codeceptionResponse = $this->useLaravel()->client->getInternalResponse();

            $illuminateResponse = new IlluminateResponse(
                $codeceptionResponse->getContent(),
                $codeceptionResponse->getStatusCode(),
                $codeceptionResponse->getHeaders()
            );

            $this->laravelResponse = TestResponse::fromBaseResponse($illuminateResponse);
        }

        return $this->laravelResponse;
    }
}

We'll use this getLaravelResponse method in our Codeception tests to gain access to a number of familiar Laravel assertions such as assertSuccessful or assertJson.

Let's go ahead and write a new functional test in Codeception.

# tests/functional/UserApiCest.php
<?php
class UserApiCest
{
    public function canGetUsers(FunctionalTester $I)
    {
        $I->sendAjaxGetRequest('/api/users');
        $I->getLaravelResponse()->assertSuccessful();
        $I->getLaravelResponse()->assertJson(['name' => 'Jeff Lebowski']);
    }
}

Step 3) Terminal Feedback

So this test passes. We're succesfully using Laravel assertions. But it's still not perfect. Let's look at the output we get in our terminal from this test:

UserApiCest: Can get users
Signature: UserApiCest:canGetUsers
Test: tests/functional/UserApiCest.php:canGetUsers
Scenario --
 I send ajax get request "/api/users"
 I get laravel response
 I get laravel response
 PASSED

The feedback from our assertions reads "I get laravel response", which isn't representative of what we're doing. To help improve on this let's create some new test helper methods.

# tests/_support/Helper/Functional.php
    public function assertSuccessful()
    {
        $this->getLaravelResponse()->assertSuccessful();
    }

    public function assertJson(array $data, $strict = false)
    {
        $this->getLaravelResponse()->assertJson($data, $strict);
    }

Then we'll update our test to match...

# tests/functional/UserApiCest.php
<?php
class UserApiCest
{
    public function canGetUsers(FunctionalTester $I)
    {
        $I->sendAjaxGetRequest('/api/users');
        $I->assertSuccessful();
        $I->assertJson(['name' => 'Jeff Lebowski']);
    }
}

Our Codeception test now looks and feels similar to our Laravel test. More importantly, the feedback in our terminal is more clear.

UserApiCest: Can get users
Signature: UserApiCest:canGetUsers
Test: tests/functional/UserApiCest.php:canGetUsers
Scenario --
 I send ajax get request "/api/users"
 I assert successful
 I assert json {"name":"Jeff Lebowski"}
 PASSED

Quick shout out to @FatBoyXPC for figuring this one out.

Join my mailing list

Subscribe now to stay up to date on whatever cool stuff I'm working on.

View Past Newsletters