Now that we’ve written some acceptance tests, functional tests are almost the same, with one major difference: functional tests don’t require a web server to run tests.
In simple terms we set the $_REQUEST
, $_GET
and $_POST
variables and then we execute the application from a test. This may be valuable as functional tests are faster and provide detailed stack traces on failures.
Codeception can connect to different web frameworks that support functional testing: Symfony2, Laravel5, Yii2, Zend Framework and others. You just need to enable the desired module in your functional suite configuration to start.
Modules for all of these frameworks share the same interface, and thus your tests are not bound to any one of them. This is a sample functional test:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->click('Login'); $I->fillField('Username', 'Miles'); $I->fillField('Password', 'Davis'); $I->click('Enter'); $I->see('Hello, Miles', 'h1'); // $I->seeEmailIsSent() - special for Symfony2
As you see, you can use same tests for functional and acceptance testing.
Acceptance tests are usually much slower than functional tests. But functional tests are less stable as they run Codeception and the application in the one environment. If your application was not designed to run in long lived processes, for instance you use the exit
operator or global variables, then probably functional tests are not for you.
One of the common issues with functional tests is the use of PHP functions that deal with headers
, sessions
and cookies
. As you may already know, the header
function triggers an error if it is executed after PHP has already output something. In functional tests we run the application multiple times, thus we will get lots of irrelevant errors in the result.
In functional testing, unlike running the application the traditional way, the PHP application does not stop after it has finished processing a request. Since all requests are run in the one memory container, they are not isolated. So if you see that your tests are mysteriously failing when they shouldn’t - try to execute a single test. This will see if the tests were failing because they weren’t isolated during the run. Keep your memory clean, avoid memory leaks and clean global and static variables.
You have a functional testing suite in the tests/functional
directory. To start, you need to include one of the framework modules in the suite configuration file: tests/functional.suite.yml
. Bellow we provide simple instructions for setting up functional tests with some of the most popular PHP frameworks.
To perform Symfony integrations you don’t need to install any bundles or do any configuration changes. You just need to include the Symfony
module into your test suite. If you also use Doctrine2, don’t forget to include it too. To make the Doctrine2 module connect using the doctrine
service from Symfony DIC, you should specify the Symfony module as a dependency for Doctrine2:
Example of functional.suite.yml
class_name: FunctionalTester modules: enabled: - Symfony - Doctrine2: depends: Symfony # connect to Symfony - \Helper\Functional
By default this module will search for App Kernel in the app
directory.
The module uses the Symfony Profiler to provide additional information and assertions.
The Laravel5 module is included and requires no configuration:
class_name: FunctionalTester modules: enabled: - Laravel5 - \Helper\Functional
Yii2 tests are included in Basic and Advanced application templates. Follow the Yii2 guides to start.
By itself Yii framework does not have an engine for functional testing. So Codeception is the first and the only functional testing framework for Yii. To use it with Yii include Yii1
module into config:
class_name: FunctionalTester modules: enabled: - Yii1 - \Helper\Functional
To avoid the common pitfalls we discussed earlier, Codeception provides basic hooks over the Yii engine. Please set them up following the installation steps in the module reference.
Use the ZF2 module to run functional tests inside Zend Framework 2:
class_name: FunctionalTester modules: enabled: - ZF2 - \Helper\Functional
The module for Zend Framework is highly inspired by the ControllerTestCase class, used for functional testing with PHPUnit. It follows similar approaches for bootstrapping and cleaning up. To start using Zend Framework in your functional tests, include the ZF1
module:
Example of functional.suite.yml
class_name: FunctionalTester modules: enabled: - ZF1 - \Helper\Functional
The Phalcon
module requires creating a bootstrap file which returns an instance of \Phalcon\Mvc\Application
. To start writing functional tests with Phalcon support you should enable the Phalcon
module and provide the path to this bootstrap file:
class_name: FunctionalTester modules: enabled: - Phalcon: bootstrap: 'app/config/bootstrap.php' cleanup: true savepoints: true - \Helper\Functional
Functional tests are written in the same manner as Acceptance Tests with the PhpBrowser
module enabled. All framework modules and the PhpBrowser
module share the same methods and the same engine.
Therefore we can open a web page with amOnPage
command:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/login');
We can click links to open web pages of application:
<?php $I->click('Logout'); // click link inside .nav element $I->click('Logout', '.nav'); // click by CSS $I->click('a.logout'); // click with strict locator $I->click(['class' => 'logout']);
We can submit forms as well:
<?php $I->submitForm('form#login', ['name' => 'john', 'password' => '123456']); // alternatively $I->fillField('#login input[name=name]', 'john'); $I->fillField('#login input[name=password]', '123456'); $I->click('Submit', '#login');
And do assertions:
<?php $I->see('Welcome, john'); $I->see('Logged in successfully', '.notice'); $I->seeCurrentUrlEquals('/profile/john');
Framework modules also contain additional methods to access framework internals. For instance, Laravel5
, Phalcon
, and Yii2
modules have seeRecord
method which uses ActiveRecord layer to check that record exists in database.
Take a look at the complete reference for module you are using. Most of its methods are common for all modules but some of them are unique.
You can also access framework globals inside a test or access dependency injection containers inside the Helper\Functional
class:
<?php namespace Helper; class Functional extends \Codeception\Module { function doSomethingWithMyService() { $service = $this->getModule('Symfony')->grabServiceFromContainer('myservice'); $service->doSomething(); } }
Also check all available Public Properties of the used modules to get full access to their data.
By default Codeception uses the E_ALL & ~E_STRICT & ~E_DEPRECATED
error reporting level. In functional tests you might want to change this level depending on your framework’s error policy. The error reporting level can be set in the suite configuration file:
class_name: FunctionalTester modules: enabled: - Yii1 - \Helper\Functional error_level: "E_ALL & ~E_STRICT & ~E_DEPRECATED"
error_level
can be set globally in codeception.yml
file.
Functional tests are great if you are using powerful frameworks. By using functional tests you can access and manipulate their internal state. This makes your tests shorter and faster. In other cases, if you don’t use frameworks there is no practical reason to write functional tests. If you are using a framework other than the ones listed here, create a module for it and share it with community.
© 2011–2017 Michael Bodnarchuk and contributors
Licensed under the MIT License.
http://codeception.com/docs/04-FunctionalTests