Codeception for Yii Framework

Yii Framework provides basic and advanced application templates. Both include sample Codeception tests, thus to start with Codeception you need to start new Yii project from a one of those templates.

Yii Basic

Once you created a project from a basic template you should see folder tests and codeception.yml configuration file. From start there are functional and unit test suites.

Run them by executing in terminal:

./vendor/bin/codecept run

Output

Unit Tests

Unit tests are located in tests/unit directory and are supposed to contain all kind of unit and integration testing.

Each test case extends Codeception\Test\Unit class, which is standard Codeception format for unit testing. It is pretty hard to develop completely isolated unit tests in Yii, so an application is bootstrapped before each test case. Tests are configured in tests/unit.suite.yml file with Yii2 module enabled:

modules:
    enabled:
      - Yii2:
            part: [orm, email]

This module starts Yii application for a test case and provides additional helper methods to simplify testing. It has only orm and email parts in order to exclude methods needed for functional testing only.

By accessing $this->tester class inside a test case you can use methods of Yii2 module. So if you have orm and email parts enabled so you can call methods belonging from these parts:

<?php
// insert records in database
$this->tester->haveRecord('app/model/User', ['username' => 'davert']);
// check records in database
$this->tester->seeRecord('app/model/User', ['username' => 'davert']);
// test email was sent
$this->tester->seeEmailIsSent();
// get a last sent emails
$this->tester->grabLastSentEmail();

If you enable fixtures part you will also get methods to load and use fixtures in your tests:

<?php
// load fixtures
$this->tester->haveFixtures([
    'user' => [
        'class' => UserFixture::className(),
        // fixture data located in tests/_data/user.php
        'dataFile' => codecept_data_dir() . 'user.php'
    ]
]);
// get first user from fixtures
$this->tester->grabFixture('user', 0);

If Yii2 module is enabled you can safely call Yii::$app inside a test, as application is initialized and cleaned up after a test. If you want to add your helper methods or custom assertions for your test case you should not extend Codeception\Test\Unit but write your own separate Helper class.

Continue to Unit Testing Guide »

Functional Tests

When it comes to test real features of web applications you can’t go with unit testing only. You want to test how application handles the requests, what responses it provides, what data is saved to database and so on. To test application in near user environment but without launching real webserver or a browser you can use functional tests. They are far more simpler than unit tests in a way they are written. They describe interaction scenario in a simple DSL so you don’t need to deal with application directly but describe actions from a user’s perspective:

<?php
$I->amOnPage(['site/contact']);
$I->submitForm('#contact-form', []);
$I->expectTo('see validations errors');
$I->see('Contact', 'h1');
$I->see('Name cannot be blank');
$I->see('Email cannot be blank');
$I->see('Subject cannot be blank');
$I->see('Body cannot be blank');

This way you not only test the ContactForm on a site but actual output of application that user sees. Codeception provides standard set of actions like amOnPage, submitForm, see for testing. Yii2 module provides special methods, like amLoggedInAs (for fast authentication), haveRecord, seeRecord, seeEmailIsSent and others. They all are listed in module reference.

Functional tests should be written inside Cest files, which is a scenario-driven test format of Codeception. You can easily create a new test by running:

./vendor/bin/codecept g:cest functional MyNewScenarioCest
Functional tests are really powerful and simple. They are highly recommended to use for any Yii application.
Continue to Functional Testing Guide »

API Tests

API tests are not included in any Yii templates so you need to set up them manually if you developing a web service. API testing is done at functional testing level but instead of testing HTML responses on user actions, they test requests and responses via protocols like REST or SOAP. To start writing api tests you should create a suite for them

./vendor/bin/codecept g:suite api

You will need to enable REST, Yii2 module in tests/api.suite.yml:

class_name: ApiTester
modules:
    enabled:
        - REST:
            url: /api/v1
            depends: Yii2
        - \ApiBundle\Helper\Api
    config:
        - Yii2

Yii2 module actions like amOnPage or see should not be available for testing API. This is why Yii2 module is not enabled but declared with depends for REST module.

Acceptance Tests

From a test perspective acceptance tests do the same as functional tests. They test the user interaction with application but in this case using real browser and web server. They are much slower and much more fragile. They should not duplicate functional tests in matter of testing functionality but should be used for testing the UI of your application. If you are unsure which tests should be acceptance and which are functional, write acceptance tests for JavaScript-rich applications, where UI highly depends on a browser processing. You can also use acceptance tests for happy-path scenarios, just to ensure that a real user using a real browser achieve the same results you expect in functional tests.

By default in basic application acceptance tests are disabled (as they require web server, Selenium Server and browser to be running). You can easily enable them by renaming acceptance.suite.yml.example to acceptance.suite.yml

mv tests/acceptance.suite.yml.example tests/acceptance.suite.yml

Basic template uses codeception/base package which doesn’t contain facebook/webdriver library required to run acceptance tests. Please change codeception/base to codeception/codeception in composer.json and run the update command.

Then you will need to launch application server in test mode:

./tests/bin/yii serve

and start a Selenium Server. For acceptance WebDriver module is used. Please check its reference to learn how to work with it. Unlike Yii2 module it does know nothing about your application, so if you want to use features of Yii like fixtures for acceptance testing, you should check that enable Yii2 module is enabled as well:

# config at tests/acceptance.yml
modules:
    enabled:
        - WebDriver:
            url: http://127.0.0.1:8080/
            browser: firefox
        - Yii2:
            part: [orm, fixtures] # allow to use AR methods
            cleanup: false # don't wrap test in transaction
            entryScript: index-test.php

As it was said, functional and acceptance tests are similar, so in order to avoid conflicts with these modules you should load only a part of Yii2 module which you really need. You must also set cleanup: false so Yii2 changes to database to be saved and used by application running on web server. Use entryScript and entryUrl values to change the default host and script configuration for your app.

Similar as for functional tests it is recommended to use Cest format for acceptance testing:

./vendor/bin/codecept g:cest acceptance MyNewScenarioCest

Yii Advanced

Advanced template also has Codeception preinstalled with examples of unit, functional, and acceptance tests. However, you won’t find tests folder in a root of application. This because every application frontend, backend, and their common has their own tests. This is done to have tests and source to be placed in a same location. To run all tests from all application at once, you should execute codecept run from a project root. Global codeception.yml config was written to include tests from all applications.

Tests also include namespaces for testcase classes and testers to avoid conflicts between tests from different applications.

Manual Setup && Configuration

To start you need to install Codeception via Composer

composer require "codeception/codeception" --dev

Create basic test suites

./vendor/bin/codecept bootstrap

Enable module Yii2 for functional tests inside functional.suite.yml:

# functional.suite.yml
modules:
    enabled:
        - Yii2:
            configFile: #insert path to config file

The only required parameter for Yii2 module is configFile. This file with configuration for test configuration of Yii application. It should merge original application config overriding id value and provide different database for testing:

<?php
// config/test.php
$config =  yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/main.php'),
    require(__DIR__ . '/main-local.php'),
    [
        'id' => 'app-tests',
        'components' => [
            'db' => [
                'dsn' => 'mysql:host=localhost;dbname=yii_app_test',
            ]
        ]        
    ]
);
return $config;

Test config is recommended to store in config folder of application. You should provide path to test config relatively to codeception.yml file.

Please also make sure that YII_ENV constant is set to test as it is done in tests/_bootstrap.php file of basic and advanced app templates.

Once you configured functional tests it should be easy to create setup for unit and acceptance tests, as it is described in this guide.

In basic and advanced application templates configFile is defined in global configuration file:

# inside codeception.yml
modules:
    config:
        Yii2:
            configFile: 'config/test.php'

This way we don’t need to provide test config for each defined suite.