June 5, 2020 by Glenn
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.
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');
}
}
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']);
}
}
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.