This commit is contained in:
2020-10-06 14:27:47 +07:00
commit 586be80cf6
16613 changed files with 3274099 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
# Introduction
The idea behind testing is not new. You can't sleep well if you are not confident
that your last commit didn't take down the whole application.
Having your application covered with tests gives you more trust in the stability of your application. That's all.
In most cases tests don't guarantee that the application works 100% as it is supposed to.
You can't predict all possible scenarios and exceptional situations for complex apps,
but with tests you can cover the most important parts of your app and at least be sure they work as predicted.
There are plenty of ways to test your application.
The most popular paradigm is [Unit Testing](http://en.wikipedia.org/wiki/Unit_testing).
For web applications, testing just the controller and/or the model doesn't prove that your application is working.
To test the behavior of your application as a whole, you should write functional or acceptance tests.
Codeception supports all three testing types.
Out of the box you have tools for writing unit, functional, and acceptance tests in a unified framework.
| | Codeception Unit Tests | Codeception Functional Tests | Codeception Acceptance Tests
| --- | --- | --- | --- |
| Scope of the test | Single PHP class | PHP Framework (Routing, Controllers, etc.) | Page in browser (Chrome, Firefox, or PhpBrowser) |
| Testing computer needs access to project's PHP files | Yes | Yes | No |
| Webserver required | No | No | Yes |
| JavaScript | No | No | Yes |
| Additional software required | None | None | <ul><li>For WebDriver: Selenium Server or PhantomJS (deprecated)</li><li>For PhpBrowser: None</li></ul> |
| Test execution speed | High | High | Low |
| Configuration file | `unit.suite.yml` | `functional.suite.yml` | `acceptance.suite.yml` |
One of the main advantages of Codeception is that you don't have to decide on just *one* type of testing. You can have all three!
And chances are, that you will (sooner or later) need all three. That's why Codeception consists of three so-called "suites":
A "unit suite" for all unit tests, a "functional suite" for all functional tests, and an "acceptance suite" for all acceptance tests.
Let's review those three testing types in reverse order.
### Acceptance Tests
How does your client, manager, tester, or any other non-technical person know your website is working?
By opening the browser, accessing the site, clicking on links, filling in the forms,
and actually seeing the content on a web page. They have no idea of the programming language, framework, database, web-server,
or why the application did (or did not) behave as expected.
This is what acceptance tests are doing. They cover scenarios from a user's perspective.
With acceptance tests, you can be confident that users, following all the defined scenarios, won't get errors.
**Any website** can be covered with acceptance tests, even if you use a very exotic CMS or framework.
#### Sample acceptance test
```php
<?php
$I->amOnPage('/');
$I->click('Sign Up');
$I->submitForm('#signup', ['username' => 'MilesDavis', 'email' => 'miles@davis.com']);
$I->see('Thank you for Signing Up!');
```
### Functional Tests
What if we could check our application without running it on a server?
That way we could see detailed exceptions on errors, have our tests run faster,
and check the database against predictable and expected results. That's what functional tests are for.
For functional tests, you emulate a web request (`$_GET` and `$_POST` variables)
which returns the HTML response. Inside a test, you can make assertions about the response,
and you can check if the data was successfully stored in the database.
For functional tests, your application needs to be structured in order to run in a test environment.
Codeception provides connectors to several popular PHP frameworks. You can also write your own.
#### Sample functional test
```php
<?php
$I->amOnPage('/');
$I->click('Sign Up');
$I->submitForm('#signup', ['username' => 'MilesDavis', 'email' => 'miles@davis.com']);
$I->see('Thank you for Signing Up!');
$I->seeEmailSent('miles@davis.com', 'Thank you for registration');
$I->seeInDatabase('users', ['email' => 'miles@davis.com']);
```
### Unit Tests
Testing pieces of code before coupling them together is highly important as well. This way,
you can be sure that some deeply hidden feature still works, even if it was not covered by functional or acceptance tests.
This also shows care in producing stable and testable code.
Codeception is created on top of [PHPUnit](http://www.phpunit.de/). If you have experience writing unit tests with PHPUnit
you can continue doing so. Codeception has no problem executing standard PHPUnit tests,
but, additionally, Codeception provides some well-built tools to make your unit tests simpler and cleaner.
Requirements and code can change rapidly,
and unit tests should be updated every time to fit the requirements.
The better you understand the testing scenario, the faster you can update it for new behavior.
#### Sample integration test
```php
<?php
function testSavingUser()
{
$user = new User();
$user->setName('Miles');
$user->setSurname('Davis');
$user->save();
$this->assertEquals('Miles Davis', $user->getFullName());
$this->tester->seeInDatabase('users', ['name' => 'Miles', 'surname' => 'Davis']);
}
```
## Conclusion
Despite the wide popularity of *TDD* (Test Driven Development), some PHP developers never write automated tests for their applications mostly because they think it's hard, slow or boring.
The Codeception framework was developed to actually make testing fun.
It allows writing unit, functional, integration, and acceptance tests in a single, coherent style.
It can be called a *BDD* (Behavior Driven Development) framework. All Codeception tests are written in a descriptive manner.
Just by looking at the test body, you can clearly understand what is being tested and how it is performed.
Even complex tests with many assertions are written in a simple PHP Domain-Specific Language (*DSL*).

View File

@@ -0,0 +1,331 @@
# Getting Started
Let's take a look at Codeception's architecture. We'll assume that you have already [installed](http://codeception.com/install) it
and bootstrapped your first test suites. Codeception has generated three of them: unit, functional, and acceptance.
They are well described in the [previous chapter](http://codeception.com/docs/01-Introduction). Inside your __/tests__ folder you will have three `.yml` config files and three directories
with names corresponding to these suites: `unit`, `functional`, `acceptance`. Suites are independent groups of tests with a common purpose.
## The Codeception Syntax
Codeception follows simple naming rules to make it easy to remember (as well as easy to understand) its method names.
* **Actions** start with a plain english verb, like "click" or "fill". Examples:
```php
<?php
$I->click('Login');
$I->fillField('#input-username', 'John Dough');
$I->pressKey('#input-remarks', 'foo');
```
* **Assertions** always start with "see" or "dontSee". Examples:
```php
<?php
$I->see('Welcome');
$I->seeInTitle('My Company');
$I->seeElement('nav');
$I->dontSeeElement('#error-message');
$I->dontSeeInPageSource('<section class="foo">');
```
* **Grabbers** just *read* something from the page, but don't process it. The return value of those are meant to be saved as variables and used later. Example:
```php
<?php
$method = $I->grabAttributeFrom('#login-form', 'method');
$I->assertEquals('post', $method);
```
## Actors
One of the main concepts of Codeception is representation of tests as actions of a person.
We have a UnitTester, who executes functions and tests the code. We also have a FunctionalTester, a qualified tester,
who tests the application as a whole, with knowledge of its internals. Lastly we have an AcceptanceTester, a user who works with our application
through an interface that we provide.
**Methods of actor classes are generally taken from [Codeception Modules](http://codeception.com/docs/06-ModulesAndHelpers)**.
Each module provides predefined actions for different testing purposes, and they can be combined to fit the testing environment.
Codeception tries to solve 90% of possible testing issues in its modules, so you don't have to reinvent the wheel.
We think that you can spend more time on writing tests and less on writing support code to make those tests run.
By default, AcceptanceTester relies on PhpBrowser module, which is set in the `tests/acceptance.suite.yml` configuration file:
```yaml
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: http://localhost/myapp/
- \Helper\Acceptance
```
In this configuration file you can enable/disable and reconfigure modules for your needs.
When you change the configuration, the actor classes are rebuilt automatically. If the actor classes are not created or updated as you expect,
try to generate them manually with the `build` command:
```bash
php vendor/bin/codecept build
```
## Writing a Sample Test
Codeception has its own testing format called Cest (Codecept + Test).
To start writing a test we need to create a new Cest file. We can do that by running the following command:
```bash
php vendor/bin/codecept generate:cest acceptance Signin
```
This will generate `SigninCest.php` file inside `tests/acceptance` directory. Let's open it:
```php
<?php
class SigninCest
{
function _before(AcceptanceTester $I)
{
}
public function _after(AcceptanceTester $I)
{
}
public function tryToTest(AcceptanceTester $I)
{
// todo: write test
}
}
```
We have `_before` and `_after` methods to run some common actions before and after a test. And we have a placeholder action `tryToTest` which we need to implement.
If we try to test a signin process it's a good start to test a successful signin. Let's rename this method to `signInSuccessfully`.
We'll assume that we have a 'login' page where we get authenticated by providing a username and password.
Then we are sent to a user page, where we see the text `Hello, %username%`. Let's look at how this scenario is written in Codeception:
```php
<?php
class SigninCest
{
public function loginSuccessfully(AcceptanceTester $I)
{
$I->amOnPage('/login');
$I->fillField('Username','davert');
$I->fillField('Password','qwerty');
$I->click('Login');
$I->see('Hello, davert');
}
}
```
This scenario can probably be read by non-technical people. If you just remove all special chars like braces, arrows and `$`,
this test transforms into plain English text:
```
I amOnPage '/login'
I fillField 'Username','davert'
I fillField 'Password','qwerty'
I click 'Login'
I see 'Hello, davert'
```
Codeception generates this text representation from PHP code by executing:
```bash
php vendor/bin/codecept generate:scenarios
```
These generated scenarios will be stored in your `_data` directory in text files.
Before we execute this test, we should make sure that the website is running on a local web server.
Let's open the `tests/acceptance.suite.yml` file and replace the URL with the URL of your web application:
```yaml
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: 'http://myappurl.local'
- \Helper\Acceptance
```
After configuring the URL we can run this test with the `run` command:
```bash
php vendor/bin/codecept run
```
This is the output we should see:
```bash
Acceptance Tests (1) -------------------------------
✔ SigninCest: sign in successfully
----------------------------------------------------
Time: 1 second, Memory: 21.00Mb
OK (1 test, 1 assertions)
```
Let's get some detailed output:
```bash
php vendor/bin/codecept run acceptance --steps
```
We should see a step-by-step report on the performed actions:
```bash
Acceptance Tests (1) -------------------------------
SigninCest: Login to website
Signature: SigninCest.php:signInSuccessfully
Test: tests/acceptance/SigninCest.php:signInSuccessfully
Scenario --
I am on page "/login"
I fill field "Username" "davert"
I fill field "Password" "qwerty"
I click "Login"
I see "Hello, davert"
OK
----------------------------------------------------
Time: 0 seconds, Memory: 21.00Mb
OK (1 test, 1 assertions)
```
This simple test can be extended to a complete scenario of site usage, therefore,
by emulating the user's actions, you can test any of your websites.
To run more tests create a public method for each of them. Include `AcceptanceTester` object as `$I` as a method parameter and use the same `$I->` API you've seen before.
If your tests share common setup actions put them into `_before` method.
For instance, to test CRUD we want 4 methods to be implemented and all next tests should start at `/task` page:
```php
<?php
class TaskCrudCest
{
function _before(AcceptanceTester $I)
{
// will be executed at the beginning of each test
$I->amOnPage('/task');
}
function createTask(AcceptanceTester $I)
{
// todo: write test
}
function viewTask(AcceptanceTester $I)
{
// todo: write test
}
function updateTask(AcceptanceTester $I)
{
// todo: write test
}
function deleteTask(AcceptanceTester $I)
{
// todo: write test
}
}
```
Learn more about the [Cest format](http://codeception.com/docs/07-AdvancedUsage#Cest-Classes) in the Advanced Testing section.
## BDD
Codeception allows execution of user stories in Gherkin format in a similar manner as is done in Cucumber or Behat.
Please refer to [the BDD chapter](http://codeception.com/docs/07-BDD) to learn more.
## Configuration
Codeception has a global configuration in `codeception.yml` and a config for each suite. We also support `.dist` configuration files.
If you have several developers in a project, put shared settings into `codeception.dist.yml` and personal settings into `codeception.yml`.
The same goes for suite configs. For example, the `unit.suite.yml` will be merged with `unit.suite.dist.yml`.
## Running Tests
Tests can be started with the `run` command:
```bash
php vendor/bin/codecept run
```
With the first argument you can run all tests from one suite:
```bash
php vendor/bin/codecept run acceptance
```
To limit tests run to a single class, add a second argument. Provide a local path to the test class, from the suite directory:
```bash
php vendor/bin/codecept run acceptance SigninCest.php
```
Alternatively you can provide the full path to test file:
```bash
php vendor/bin/codecept run tests/acceptance/SigninCest.php
```
You can further filter which tests are run by appending a method name to the class, separated by a colon (for Cest or Test formats):
```bash
php vendor/bin/codecept run tests/acceptance/SigninCest.php:^anonymousLogin$
```
You can provide a directory path as well. This will execute all acceptance tests from the `backend` dir:
```bash
php vendor/bin/codecept run tests/acceptance/backend
```
Using regular expressions, you can even run many different test methods from the same directory or class.
For example, this will execute all acceptance tests from the `backend` dir beginning with the word "login":
```bash
php vendor/bin/codecept run tests/acceptance/backend:^login
```
To execute a group of tests that are not stored in the same directory, you can organize them in [groups](http://codeception.com/docs/07-AdvancedUsage#Groups).
### Reports
To generate JUnit XML output, you can provide the `--xml` option, and `--html` for HTML report.
```bash
php vendor/bin/codecept run --steps --xml --html
```
This command will run all tests for all suites, displaying the steps, and building HTML and XML reports. Reports will be stored in the `tests/_output/` directory.
To see all the available options, run the following command:
```bash
php vendor/bin/codecept help run
```
## Debugging
To receive detailed output, tests can be executed with the `--debug` option.
You may print any information inside a test using the `codecept_debug` function.
### Generators
There are plenty of useful Codeception commands:
* `generate:cest` *suite* *filename* - Generates a sample Cest test
* `generate:test` *suite* *filename* - Generates a sample PHPUnit Test with Codeception hooks
* `generate:feature` *suite* *filename* - Generates Gherkin feature file
* `generate:suite` *suite* *actor* - Generates a new suite with the given Actor class name
* `generate:scenarios` *suite* - Generates text files containing scenarios from tests
* `generate:helper` *filename* - Generates a sample Helper File
* `generate:pageobject` *suite* *filename* - Generates a sample Page object
* `generate:stepobject` *suite* *filename* - Generates a sample Step object
* `generate:environment` *env* - Generates a sample Environment configuration
* `generate:groupobject` *group* - Generates a sample Group Extension
## Conclusion
We have taken a look into the Codeception structure. Most of the things you need were already generated by the `bootstrap` command.
After you have reviewed the basic concepts and configurations, you can start writing your first scenario.

View File

@@ -0,0 +1,637 @@
# Acceptance Testing
Acceptance testing can be performed by a non-technical person. That person can be your tester, manager or even client.
If you are developing a web-application (and you probably are) the tester needs nothing more than a web browser
to check that your site works correctly. You can reproduce an acceptance tester's actions in scenarios
and run them automatically. Codeception keeps tests clean and simple
as if they were recorded from the words of an actual acceptance tester.
It makes no difference what (if any) CMS or framework is used on the site. You can even test sites created with different
languages, like Java, .NET, etc. It's always a good idea to add tests to your website.
At least you will be sure that site features work after the latest changes were made.
## Sample Scenario
Let's say the first test you would want to run, would be signing in.
In order to write such a test, we still require basic knowledge of PHP and HTML:
```php
<?php
$I->amOnPage('/login');
$I->fillField('username', 'davert');
$I->fillField('password', 'qwerty');
$I->click('LOGIN');
$I->see('Welcome, Davert!');
```
**This scenario can be performed either by PhpBrowser or by a "real" browser through Selenium WebDriver**.
| | PhpBrowser | WebDriver |
| --- | --- | --- |
| Browser Engine | Guzzle + Symfony BrowserKit | Chrome or Firefox |
| JavaScript | No | Yes |
| `see`/`seeElement` checks if… | …text is present in the HTML source | …text is actually visible to the user |
| Read HTTP response headers | Yes | No |
| System requirements | PHP with [cURL extension](http://php.net/manual/book.curl.php) | Selenium Standalone Server, Chrome or Firefox |
| Speed | Fast | Slow |
We will start writing our first acceptance tests with PhpBrowser.
## PhpBrowser
This is the fastest way to run acceptance tests since it doesn't require running an actual browser.
We use a PHP web scraper, which acts like a browser: It sends a request, then receives and parses the response.
Codeception uses [Guzzle](http://guzzlephp.org) and [Symfony BrowserKit](http://symfony.com/doc/current/components/browser_kit.html) to interact with HTML web pages.
Common PhpBrowser drawbacks:
* You can only click on links with valid URLs or form submit buttons
* You can't fill in fields that are not inside a form
We need to specify the `url` parameter in the acceptance suite config:
```yaml
# acceptance.suite.yml
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: http://www.example.com/
- \Helper\Acceptance
```
We should start by creating a test with the next command:
```
php vendor/bin/codecept g:cest acceptance Signin
```
It will be placed into `tests/acceptance` directory.
```php
<?php
class SigninCest
{
public function tryToTest(AcceptanceTester $I)
{
$I->wantTo('test my page');
}
}
```
The `$I` object is used to write all interactions.
The methods of the `$I` object are taken from the [PhpBrowser Module](http://codeception.com/docs/modules/PhpBrowser). We will briefly describe them here:
```php
<?php
$I->amOnPage('/login');
```
We will assume that all actions starting with `am` and `have` describe the initial environment.
The `amOnPage` action sets the starting point of a test to the `/login` page.
With the `PhpBrowser` you can click the links and fill in the forms. That will probably be the majority of your actions.
#### Click
Emulates a click on valid anchors. The URL referenced in the `href` attribute will be opened.
As a parameter, you can specify the link name or a valid CSS or XPath selector.
```php
<?php
$I->click('Log in');
// CSS selector applied
$I->click('#login a');
// XPath
$I->click('//a[@id=login]');
// Using context as second argument
$I->click('Login', '.nav');
```
Codeception tries to locate an element by its text, name, CSS or XPath.
You can specify the locator type manually by passing an array as a parameter. We call this a **strict locator**.
Available strict locator types are:
* id
* name
* css
* xpath
* link
* class
```php
<?php
// By specifying locator type
$I->click(['link' => 'Login']);
$I->click(['class' => 'btn']);
```
There is a special class [`Codeception\Util\Locator`](http://codeception.com/docs/reference/Locator)
which may help you to generate complex XPath locators.
For instance, it can easily allow you to click an element on the last row of a table:
```php
$I->click('Edit' , \Codeception\Util\Locator::elementAt('//table/tr', -1));
```
#### Forms
Clicking links is probably not what takes the most time during the testing of a website.
The most routine waste of time goes into the testing of forms. Codeception provides several ways of testing forms.
Let's submit this sample form inside the Codeception test:
```html
<form method="post" action="/update" id="update_form">
<label for="user_name">Name</label>
<input type="text" name="user[name]" id="user_name" />
<label for="user_email">Email</label>
<input type="text" name="user[email]" id="user_email" />
<label for="user_gender">Gender</label>
<select id="user_gender" name="user[gender]">
<option value="m">Male</option>
<option value="f">Female</option>
</select>
<input type="submit" name="submitButton" value="Update" />
</form>
```
From a user's perspective, a form consists of fields which should be filled in, and then a submit button clicked:
```php
<?php
// we are using label to match user_name field
$I->fillField('Name', 'Miles');
// we can use input name or id
$I->fillField('user[email]','miles@davis.com');
$I->selectOption('Gender','Male');
$I->click('Update');
```
To match fields by their labels, you should write a `for` attribute in the `label` tag.
From the developer's perspective, submitting a form is just sending a valid POST request to the server.
Sometimes it's easier to fill in all of the fields at once and send the form without clicking a 'Submit' button.
A similar scenario can be rewritten with only one command:
```php
<?php
$I->submitForm('#update_form', array('user' => array(
'name' => 'Miles',
'email' => 'Davis',
'gender' => 'm'
)));
```
The `submitForm` is not emulating a user's actions, but it's quite useful
in situations when the form is not formatted properly, for example, to discover that labels aren't set
or that fields have unclean names or badly written IDs, or the form is sent by a JavaScript call.
By default, `submitForm` doesn't send values for buttons. The last parameter allows specifying
what button values should be sent, or button values can be explicitly specified in the second parameter:
```php
<?php
$I->submitForm('#update_form', array('user' => array(
'name' => 'Miles',
'email' => 'Davis',
'gender' => 'm'
)), 'submitButton');
// this would have the same effect, but the value has to be explicitly specified
$I->submitForm('#update_form', array('user' => array(
'name' => 'Miles',
'email' => 'Davis',
'gender' => 'm',
'submitButton' => 'Update'
)));
```
##### Hiding Sensitive Data
If you need to fill in sensitive data (like passwords) and hide it in logs,
you can pass instance `\Codeception\Step\Argument\PasswordArgument` with the data which needs to be hidden.
```php
<?php
use \Codeception\Step\Argument\PasswordArgument;
$I->amOnPage('/form/password_argument');
$I->fillField('password', new PasswordArgument('thisissecret'));
```
`thisissecret` will be filled into a form but it won't be shown in output and logs.
#### Assertions
In the `PhpBrowser` you can test the page contents.
In most cases, you just need to check that the required text or element is on the page.
The most useful method for this is `see()`:
```php
<?php
// We check that 'Thank you, Miles' is on the page.
$I->see('Thank you, Miles');
// We check that 'Thank you, Miles' is inside an element with 'notice' class.
$I->see('Thank you, Miles', '.notice');
// Or using XPath locators
$I->see('Thank you, Miles', "//table/tr[2]");
// We check this message is *not* on the page.
$I->dontSee('Form is filled incorrectly');
```
You can check that a specific HTML element exists (or doesn't) on a page:
```php
<?php
$I->seeElement('.notice');
$I->dontSeeElement('.error');
```
We also have other useful commands to perform checks. Please note that they all start with the `see` prefix:
```php
<?php
$I->seeInCurrentUrl('/user/miles');
$I->seeCheckboxIsChecked('#agree');
$I->seeInField('user[name]', 'Miles');
$I->seeLink('Login');
```
#### Conditional Assertions
Usually, as soon as any assertion fails, further assertions of this test will be skipped.
Sometimes you don't want this - maybe you have a long-running test and you want it to run to the end.
In this case, you can use conditional assertions.
Each `see` method has a corresponding `canSee` method, and `dontSee` has a `cantSee` method:
```php
<?php
$I->canSeeInCurrentUrl('/user/miles');
$I->canSeeCheckboxIsChecked('#agree');
$I->cantSeeInField('user[name]', 'Miles');
```
Each failed assertion will be shown in the test results, but it won't stop the test.
#### Comments
Within a long scenario, you should describe what actions you are going to perform and what results should be achieved.
Comment methods like `amGoingTo`, `expect`, `expectTo` help you in making tests more descriptive:
```php
<?php
$I->amGoingTo('submit user form with invalid values');
$I->fillField('user[email]', 'miles');
$I->click('Update');
$I->expect('the form is not submitted');
$I->see('Form is filled incorrectly');
```
#### Grabbers
These commands retrieve data that can be used in the test. Imagine your site generates a password for every user
and you want to check that the user can log into the site using this password:
```php
<?php
$I->fillField('email', 'miles@davis.com');
$I->click('Generate Password');
$password = $I->grabTextFrom('#password');
$I->click('Login');
$I->fillField('email', 'miles@davis.com');
$I->fillField('password', $password);
$I->click('Log in!');
```
Grabbers allow you to get a single value from the current page with commands:
```php
<?php
$token = $I->grabTextFrom('.token');
$password = $I->grabTextFrom("descendant::input/descendant::*[@id = 'password']");
$api_key = $I->grabValueFrom('input[name=api]');
```
#### Cookies, URLs, Title, etc
Actions for cookies:
```php
<?php
$I->setCookie('auth', '123345');
$I->grabCookie('auth');
$I->seeCookie('auth');
```
Actions for checking the page title:
```php
<?php
$I->seeInTitle('Login');
$I->dontSeeInTitle('Register');
```
Actions for URLs:
```php
<?php
$I->seeCurrentUrlEquals('/login');
$I->seeCurrentUrlMatches('~$/users/(\d+)~');
$I->seeInCurrentUrl('user/1');
$user_id = $I->grabFromCurrentUrl('~$/user/(\d+)/~');
```
## WebDriver
A nice feature of Codeception is that most scenarios are similar, no matter of how they are executed.
`PhpBrowser` was emulating browser requests but how to execute such test in a real browser like Chrome or Firefox?
Selenium WebDriver can drive them so in our acceptance tests we can automate scenarios we used to test manually.
In such tests, we should concentrate more on **testing the UI** than on testing functionality.
"[WebDriver](https://www.w3.org/TR/webdriver/)" is the name of a protocol (specified by W3C)
to drive browsers automatically. This specification is implemented for all modern desktop and mobile browsers.
Codeception uses [facebook/php-webdriver](https://github.com/facebook/php-webdriver) library from Facebook as PHP implementation of WebDriver protocol.
To control the browsers you need to use a program or a service to start/stop browser sessions.
In the next section, we will overview the most popular solutions.
### Local Setup
#### Selenium Server
[Selenium Server](http://www.seleniumhq.org/) is a de-facto standard for automated web and mobile testing.
It is a server that can launch and drive different browsers locally or remotely.
WebDriver protocol was initially created by Selenium before becoming a W3C standard.
This makes Selenium server the most stable complete implementation of WebDriver for today.
Selenium Server is also recommended by Codeception team.
To control browsers Selenium Server uses official tools maintained by browser vendors, like [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver) for Chrome or [GeckoDriver](https://github.com/mozilla/geckodriver) for Firefox.
This makes Selenium quite heavy to install, as it requires Java, browsers, Chrome or GeckoDriver and GUI (display server) to run browsers in.
* Follow [Installation Instructions](http://codeception.com/docs/modules/WebDriver#Selenium)
* Enable [RunProcess](http://codeception.com/extensions#RunProcess) extension to start/stop Selenium automatically *(optional)*.
#### PhantomJS
PhantomJS is a customized WebKit-based [headless browser](https://en.wikipedia.org/wiki/Headless_browser)
built for programmatic usage only. It doesn't display a browser window and doesn't require GUI (display server) to be installed.
This makes PhantomJS highly popular for Continuous Integration systems.
PhantomJS needs only one binary with no extra dependencies which make it the simplest WebDriver tool to install.
However, it should be noted that PhantomJS is not a real browser, so the behavior and output in real browsers may differ from PhantomJS.
And the most important: **PhantomJS is not maintained** anymore. So use it at your own risk.
* Follow [Installation Instructions](http://codeception.com/docs/modules/WebDriver#PhantomJS)
* Enable [RunProcess](http://codeception.com/extensions#RunProcess) extension to start/stop PhantomJS automatically *(optional)*.
#### ChromeDriver
ChromeDriver was created by Google to control Chrome and Chromium browsers programmatically.
It can be paired with [Selenium Server](http://codeception.com/docs/03-AcceptanceTests#Selenium-Server) or used as a standalone tool to drive Chrome browser.
It is simpler to set up than Selenium Server, however, it has limited support for WebDriver protocol.
* Follow [Installation Instructions](http://codeception.com/docs/modules/WebDriver#ChromeDriver)
* Enable [RunProcess](http://codeception.com/extensions#RunProcess) extension to start/stop ChromeDriver automatically *(optional)*.
### Configuration
To execute a test in a browser we need to change the suite configuration to use **WebDriver** instead of `PhpBrowser`.
Modify your `acceptance.suite.yml` file:
```yaml
actor: AcceptanceTester
modules:
enabled:
- WebDriver:
url: {{your site URL}}
browser: chrome
- \Helper\Acceptance
```
See [WebDriver Module](http://codeception.com/docs/modules/WebDriver) for details.
Please note that actions executed in a browser will behave differently. For instance, `seeElement` won't just check that the element exists on a page,
but it will also check that element is actually visible to the user:
```php
<?php
$I->seeElement('#modal');
```
While WebDriver duplicates the functionality of PhpBrowser, it has its limitations: It can't check headers since browsers don't provide APIs for that.
WebDriver also adds browser-specific functionality:
#### Wait
While testing web application, you may need to wait for JavaScript events to occur. Due to its asynchronous nature,
complex JavaScript interactions are hard to test. That's why you may need to use waiters, actions with `wait` prefix.
They can be used to specify what event you expect to occur on a page, before continuing the test.
For example:
```php
<?php
$I->waitForElement('#agree_button', 30); // secs
$I->click('#agree_button');
```
In this case, we are waiting for the 'agree' button to appear and then click it. If it didn't appear after 30 seconds,
the test will fail. There are other `wait` methods you may use, like [waitForText](http://codeception.com/docs/modules/WebDriver#waitForText),
[waitForElementVisible](http://codeception.com/docs/modules/WebDriver#waitForElementVisible) and others.
If you don't know what exact element you need to wait for, you can simply pause execution with using `$I->wait()`
```php
<?php
$I->wait(3); // wait for 3 secs
```
#### SmartWait
*since 2.3.4 version*
It is possible to wait for elements pragmatically.
If a test uses element which is not on a page yet, Codeception will wait for few extra seconds before failing.
This feature is based on [Implicit Wait](http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits) of Selenium.
Codeception enables implicit wait only when searching for a specific element and disables in all other cases. Thus, the performance of a test is not affected.
SmartWait can be enabled by setting `wait` option in WebDriver config. It expects the number of seconds to wait. Example:
```yaml
wait: 5
```
With this config we have the following test:
```php
<?php
// we use wait: 5 instead of
// $I->waitForElement(['css' => '#click-me'], 5);
// to wait for element on page
$I->click(['css' => '#click-me']);
```
It is important to understand that SmartWait works only with a specific locators:
* `#locator` - CSS ID locator, works
* `//locator` - general XPath locator, works
* `['css' => 'button'']` - strict locator, works
But it won't be executed for all other locator types.
See the example:
```php
<?php
$I->click('Login'); // DISABLED, not a specific locator
$I->fillField('user', 'davert'); // DISABLED, not a specific locator
$I->fillField(['name' => 'password'], '123456'); // ENABLED, strict locator
$I->click('#login'); // ENABLED, locator is CSS ID
$I->see('Hello, Davert'); // DISABLED, Not a locator
$I->seeElement('#userbar'); // ENABLED
$I->dontSeeElement('#login'); // DISABLED, can't wait for element to hide
$I->seeNumberOfElements(['css' => 'button.link'], 5); // DISABLED, can wait only for one element
```
#### Wait and Act
To combine `waitForElement` with actions inside that element you can use the [performOn](http://codeception.com/docs/modules/WebDriver#performOn) method.
Let's see how you can perform some actions inside an HTML popup:
```php
<?php
$I->performOn('.confirm', \Codeception\Util\ActionSequence::build()
->see('Warning')
->see('Are you sure you want to delete this?')
->click('Yes')
);
```
Alternatively, this can be executed using a callback, in this case the `WebDriver` instance is passed as argument
```php
<?php
$I->performOn('.confirm', function(\Codeception\Module\WebDriver $I) {
$I->see('Warning');
$I->see('Are you sure you want to delete this?');
$I->click('Yes');
});
```
For more options see [`performOn()` reference](http://codeception.com/docs/modules/WebDriver#performOn).
### Multi Session Testing
Codeception allows you to execute actions in concurrent sessions. The most obvious case for this
is testing realtime messaging between users on a site. In order to do it, you will need to launch two browser windows
at the same time for the same test. Codeception has a very smart concept for doing this. It is called **Friends**:
```php
<?php
$I->amOnPage('/messages');
$nick = $I->haveFriend('nick');
$nick->does(function(AcceptanceTester $I) {
$I->amOnPage('/messages/new');
$I->fillField('body', 'Hello all!');
$I->click('Send');
$I->see('Hello all!', '.message');
});
$I->wait(3);
$I->see('Hello all!', '.message');
```
In this case, we performed, or 'did', some actions in the second window with the `does` method on a friend object.
Sometimes you may want to close a webpage before the end of the test. For such cases, you may use `leave()`.
You can also specify roles for a friend:
```php
<?php
$nickAdmin = $I->haveFriend('nickAdmin', adminStep::class);
$nickAdmin->does(function(adminStep $I) {
// Admin does ...
});
$nickAdmin->leave();
```
### Cloud Testing
Some environments are hard to be reproduced manually, testing Internet Explorer 6-8 on Windows XP may be a hard thing,
especially if you don't have Windows XP installed. This is where Cloud Testing services come to help you.
Services such as [SauceLabs](https://saucelabs.com), [BrowserStack](https://www.browserstack.com/)
and [others](http://codeception.com/docs/modules/WebDriver#Cloud-Testing) can create virtual machines on demand
and set up Selenium Server and the desired browser. Tests are executed on a remote machine in a cloud,
to access local files cloud testing services provide a special application called **Tunnel**.
Tunnel operates on a secured protocol and allows browsers executed in a cloud to connect to a local web server.
Cloud Testing services work with the standard WebDriver protocol. This makes setting up cloud testing really easy.
You just need to set the [WebDriver configuration](http://codeception.com/docs/modules/WebDriver#Cloud-Testing) to:
* specify the host to connect to (depends on the cloud provider)
* authentication details (to use your account)
* browser
* OS
We recommend using [params](http://codeception.com/docs/06-ModulesAndHelpers#Dynamic-Configuration-With-Params)
to provide authorization credentials.
It should be mentioned that Cloud Testing services are not free. You should investigate their pricing models
and choose one that fits your needs. They also may work painfully slowly if ping times between the local server
and the cloud is too high. This may lead to random failures in acceptance tests.
### AngularJS Testing
In the modern era of Single Page Applications, the browser replaces the server in creating the user interface.
Unlike traditional web applications, web pages are not reloaded on user actions.
All interactions with the server are done in JavaScript with XHR requests.
However, testing Single Page Applications can be a hard task.
There could be no information of the application state: e.g. has it completed rendering or not?
What is possible to do in this case is to use more `wait*` methods or execute JavaScript that checks the application state.
For applications built with the AngularJS v1.x framework,
we implemented [AngularJS module](http://codeception.com/docs/modules/AngularJS) which is based on Protractor
(an official tool for testing Angular apps). Under the hood, it pauses step execution
before the previous actions are completed and use the AngularJS API to check the application state.
The AngularJS module extends WebDriver so that all the configuration options from it are available.
### Debugging
Codeception modules can print valuable information while running.
Just execute tests with the `--debug` option to see running details. For any custom output use the `codecept_debug` function:
```php
<?php
codecept_debug($I->grabTextFrom('#name'));
```
On each failure, the snapshot of the last shown page will be stored in the `tests/_output` directory.
PhpBrowser will store the HTML code and WebDriver will save a screenshot of the page.
Additional debugging features by Codeception:
* [pauseExecution](http://codeception.com/docs/modules/WebDriver#pauseExecution) method of WebDriver module allows pausing the test.
* [Recorder extension](http://codeception.com/addons#CodeceptionExtensionRecorder) allows to record tests step-by-steps and show them in slideshow
* [Interactive Console](http://codeception.com/docs/07-AdvancedUsage#Interactive-Console) is a REPL that allows to type and check commands for instant feedback.
### Custom Browser Sessions
By default, WebDriver module is configured to automatically start browser before the test and stop afterward.
However, this can be switched off with `start: false` module configuration.
To start a browser you will need to write corresponding methods in Acceptance [Helper](http://codeception.com/docs/06-ModulesAndHelpers#Helpers).
WebDriver module provides advanced methods for the browser session, however, they can only be used from Helpers.
* [_initializeSession](http://codeception.com/docs/modules/WebDriver#_initializeSession) - starts a new browser session
* [_closeSession](http://codeception.com/docs/modules/WebDriver#_closeSession) - stops the browser session
* [_restart](http://codeception.com/docs/modules/WebDriver#_restart) - updates configuration and restarts browser
* [_capabilities](http://codeception.com/docs/modules/WebDriver#_capabilities) - set [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) programmatically.
Those methods can be used to create custom commands like `$I->startBrowser()` or used in [before/after](http://codeception.com/docs/06-ModulesAndHelpers#Hooks) hooks.
## Conclusion
Writing acceptance tests with Codeception and PhpBrowser is a good start.
You can easily test your Joomla, Drupal, WordPress sites, as well as those made with frameworks.
Writing acceptance tests is like describing a tester's actions in PHP. They are quite readable and very easy to write.
If you need to access the database, you can use the [Db Module](http://codeception.com/docs/modules/Db).

View File

@@ -0,0 +1,274 @@
# Functional Tests
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.
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 PHP 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
<?php
// LoginCest.php
class LoginCest
{
public function tryLogin (FunctionalTester $I)
{
$I->amOnPage('/');
$I->click('Login');
$I->fillField('Username', 'Miles');
$I->fillField('Password', 'Davis');
$I->click('Enter');
$I->see('Hello, Miles', 'h1');
// $I->seeEmailIsSent(); // only for Symfony2
}
}
```
As you see, the syntax is the same for functional and acceptance tests.
## Limitations
Functional tests are usually much faster than acceptance tests. But functional tests are less stable as they run Codeception
and the application in one environment. If your application was not designed to run in long lived processes (e.g.
if you use the `exit` operator or global variables), then functional tests are probably not for you.
### Headers, Cookies, Sessions
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.
### External URL's
Functional tests cannot access external URL's, just URL's within your project. You can use Guzzle to open external URL's.
### Shared Memory
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 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 show 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.
## Enabling Framework Modules
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`.
### Symfony
To perform Symfony integration 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,
you should specify the Symfony module as a dependency for Doctrine2:
```yaml
# functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- Symfony
- Doctrine2:
depends: Symfony # connect to Symfony
- \Helper\Functional
```
By default this module will search for AppKernel in the `app` directory.
The module uses the Symfony Profiler to provide additional information and assertions.
[See the full reference](http://codeception.com/docs/modules/Symfony)
### Laravel5
The [Laravel5](http://codeception.com/docs/modules/Laravel5) module is included and requires no configuration:
```yaml
# functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- Laravel5
- \Helper\Functional
```
### Yii2
Yii2 tests are included in [Basic](https://github.com/yiisoft/yii2-app-basic)
and [Advanced](https://github.com/yiisoft/yii2-app-advanced) application templates. Follow the Yii2 guides to start.
### Yii
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:
```yaml
# functional.suite.yml
actor: 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](http://codeception.com/docs/modules/Yii1).
### Zend Framework 2
Use [the ZF2 module](http://codeception.com/docs/modules/ZF2) to run functional tests inside Zend Framework 2:
```yaml
# functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- ZF2
- \Helper\Functional
```
### Zend Framework 1.x
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:
```yaml
# functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- ZF1
- \Helper\Functional
```
[See the full reference](http://codeception.com/docs/modules/ZF1)
### Phalcon
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:
```yaml
# functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- Phalcon:
bootstrap: 'app/config/bootstrap.php'
cleanup: true
savepoints: true
- \Helper\Functional
```
[See the full reference](http://codeception.com/docs/modules/Phalcon)
## Writing Functional Tests
Functional tests are written in the same manner as [Acceptance Tests](http://codeception.com/docs/03-AcceptanceTests)
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` method:
```php
<?php
$I = new FunctionalTester($scenario);
$I->amOnPage('/login');
```
We can click links to open web pages:
```php
<?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
<?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
<?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 a `seeRecord` method which uses the ActiveRecord layer to check that a record exists in the database.
Take a look at the complete reference for the module you are using. Most of its methods are common to all modules
but some of them are unique.
You can also access framework globals inside a test or access the dependency injection container
inside the `Helper\Functional` class:
```php
<?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.
## Error Reporting
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:
```yaml
actor: FunctionalTester
modules:
enabled:
- Yii1
- \Helper\Functional
error_level: "E_ALL & ~E_STRICT & ~E_DEPRECATED"
```
`error_level` can also be set globally in `codeception.yml` file.
## Conclusion
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 the community.

View File

@@ -0,0 +1,473 @@
# Unit & Integration Tests
Codeception uses PHPUnit as a backend for running its tests. Thus, any PHPUnit test can be added to a Codeception test suite
and then executed. If you ever wrote a PHPUnit test then do it just as you did before.
Codeception adds some nice helpers to simplify common tasks.
## Creating a Test
Create a test using `generate:test` command with a suite and test names as parameters:
```bash
php vendor/bin/codecept generate:test unit Example
```
It creates a new `ExampleTest` file located in the `tests/unit` directory.
As always, you can run the newly created test with this command:
```bash
php vendor/bin/codecept run unit ExampleTest
```
Or simply run the whole set of unit tests with:
```bash
php vendor/bin/codecept run unit
```
A test created by the `generate:test` command will look like this:
```php
<?php
class ExampleTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
protected function _before()
{
}
protected function _after()
{
}
// tests
public function testMe()
{
}
}
```
Inside a class:
* all public methods with `test` prefix are tests
* `_before` method is executed before each test (like `setUp` in PHPUnit)
* `_after` method is executed after each test (like `tearDown` in PHPUnit)
## Unit Testing
Unit tests are focused around a single component of an application.
All external dependencies for components should be replaced with test doubles.
A typical unit test may look like this:
```php
<?php
class UserTest extends \Codeception\Test\Unit
{
public function testValidation()
{
$user = new User();
$user->setName(null);
$this->assertFalse($user->validate(['username']));
$user->setName('toolooooongnaaaaaaameeee');
$this->assertFalse($user->validate(['username']));
$user->setName('davert');
$this->assertTrue($user->validate(['username']));
}
}
```
### Assertions
There are pretty many assertions you can use inside tests. The most common are:
* `$this->assertEquals()`
* `$this->assertContains()`
* `$this->assertFalse()`
* `$this->assertTrue()`
* `$this->assertNull()`
* `$this->assertEmpty()`
Assertion methods come from PHPUnit. [See the complete reference at phpunit.de](https://phpunit.de/manual/current/en/appendixes.assertions.html).
### Test Doubles
Codeception provides [Codeception\Stub library](https://github.com/Codeception/Stub) for building mocks and stubs for tests.
Under the hood it used PHPUnit's mock builder but with much simplified API.
Alternatively, [Mockery](https://github.com/Codeception/MockeryModule) can be used inside Codeception.
#### Stubs
Stubs can be created with a static methods of `Codeception\Stub`.
```php
<?php
$user = \Codeception\Stub::make('User', ['getName' => 'john']);
$name = $user->getName(); // 'john'
```
[See complete reference](http://codeception.com/docs/reference/Mock)
Inside unit tests (`Codeception\Test\Unit`) it is recommended to use alternative API:
```php
<?php
// create a stub with find method replaced
$userRepository = $this->make(UserRepository::class, ['find' => new User]);
$userRepository->find(1); // => User
// create a dummy
$userRepository = $this->makeEmpty(UserRepository::class);
// create a stub with all methods replaced except one
$user = $this->makeEmptyExcept(User::class, 'validate');
$user->validate($data);
// create a stub by calling constructor and replacing a method
$user = $this->construct(User::class, ['name' => 'davert'], ['save' => false]);
// create a stub by calling constructor with empty methods
$user = $this->constructEmpty(User::class, ['name' => 'davert']);
// create a stub by calling constructor with empty methods
$user = $this->constructEmptyExcept(User::class, 'getName', ['name' => 'davert']);
$user->getName(); // => davert
$user->setName('jane'); // => this method is empty
```
[See complete reference](http://codeception.com/docs/reference/Mock)
Stubs can also be created using static methods from `Codeception\Stub` class.
In this
```php
<?php
\Codeception\Stub::make(UserRepository::class, ['find' => new User]);
```
See a reference for [static Stub API](http://codeception.com/docs/reference/Stub)
#### Mocks
To declare expectations for mocks use `Codeception\Stub\Expected` class:
```php
<?php
// create a mock where $user->getName() should never be called
$user = $this->make('User', [
'getName' => Expected::never(),
'someMethod' => function() {}
]);
$user->someMethod();
// create a mock where $user->getName() should be called at least once
$user = $this->make('User', [
'getName' => Expected::atLeastOnce('Davert')
]
);
$user->getName();
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
```
[See complete reference](http://codeception.com/docs/reference/Mock)
## Integration Tests
Unlike unit tests integration tests doesn't require the code to be executed in isolation.
That allows us to use database and other components inside a tests.
To improve the testing experience modules can be used as in functional testing.
### Using Modules
As in scenario-driven functional or acceptance tests you can access Actor class methods.
If you write integration tests, it may be useful to include the `Db` module for database testing.
```yaml
# Codeception Test Suite Configuration
# suite for unit (internal) tests.
actor: UnitTester
modules:
enabled:
- Asserts
- Db
- \Helper\Unit
```
To access UnitTester methods you can use the `UnitTester` property in a test.
### Testing Database
Let's see how you can do some database testing:
```php
<?php
function testSavingUser()
{
$user = new User();
$user->setName('Miles');
$user->setSurname('Davis');
$user->save();
$this->assertEquals('Miles Davis', $user->getFullName());
$this->tester->seeInDatabase('users', ['name' => 'Miles', 'surname' => 'Davis']);
}
```
To enable the database functionality in unit tests, make sure the `Db` module is included
in the `unit.suite.yml` configuration file.
The database will be cleaned and populated after each test, the same way it happens for acceptance and functional tests.
If that's not your required behavior, change the settings of the `Db` module for the current suite. See [Db Module](http://codeception.com/docs/modules/Db)
### Interacting with the Framework
You should probably not access your database directly if your project already uses ORM for database interactions.
Why not use ORM directly inside your tests? Let's try to write a test using Laravel's ORM Eloquent.
For this we need to configure the Laravel5 module. We won't need its web interaction methods like `amOnPage` or `see`,
so let's enable only the ORM part of it:
```yaml
actor: UnitTester
modules:
enabled:
- Asserts
- Laravel5:
part: ORM
- \Helper\Unit
```
We included the Laravel5 module the same way we did for functional testing.
Let's see how we can use it for integration tests:
```php
<?php
function testUserNameCanBeChanged()
{
// create a user from framework, user will be deleted after the test
$id = $this->tester->haveRecord('users', ['name' => 'miles']);
// access model
$user = User::find($id);
$user->setName('bill');
$user->save();
$this->assertEquals('bill', $user->getName());
// verify data was saved using framework methods
$this->tester->seeRecord('users', ['name' => 'bill']);
$this->tester->dontSeeRecord('users', ['name' => 'miles']);
}
```
A very similar approach can be used for all frameworks that have an ORM implementing the ActiveRecord pattern.
In Yii2 and Phalcon, the methods `haveRecord`, `seeRecord`, `dontSeeRecord` work in the same way.
They also should be included by specifying `part: ORM` in order to not use the functional testing actions.
If you are using Symfony with Doctrine, you don't need to enable Symfony itself but just Doctrine2:
```yaml
actor: UnitTester
modules:
enabled:
- Asserts
- Doctrine2:
depends: Symfony
- \Helper\Unit
```
In this case you can use the methods from the Doctrine2 module, while Doctrine itself uses the Symfony module
to establish connections to the database. In this case a test might look like:
```php
<?php
function testUserNameCanBeChanged()
{
// create a user from framework, user will be deleted after the test
$id = $this->tester->haveInRepository(User::class, ['name' => 'miles']);
// get entity manager by accessing module
$em = $this->getModule('Doctrine2')->em;
// get real user
$user = $em->find(User::class, $id);
$user->setName('bill');
$em->persist($user);
$em->flush();
$this->assertEquals('bill', $user->getName());
// verify data was saved using framework methods
$this->tester->seeInRepository(User::class, ['name' => 'bill']);
$this->tester->dontSeeInRepository(User::class, ['name' => 'miles']);
}
```
In both examples you should not be worried about the data persistence between tests.
The Doctrine2 and Laravel5 modules will clean up the created data at the end of a test.
This is done by wrapping each test in a transaction and rolling it back afterwards.
### Accessing Module
Codeception allows you to access the properties and methods of all modules defined for this suite.
Unlike using the UnitTester class for this purpose, using a module directly grants you access
to all public properties of that module.
We have already demonstrated this in a previous example where we accessed the Entity Manager from a Doctrine2 module:
```php
<?php
/** @var Doctrine\ORM\EntityManager */
$em = $this->getModule('Doctrine2')->em;
```
If you use the `Symfony` module, here is how you can access the Symfony container:
```php
<?php
/** @var Symfony\Component\DependencyInjection\Container */
$container = $this->getModule('Symfony')->container;
```
The same can be done for all public properties of an enabled module. Accessible properties are listed in the module reference.
### Scenario Driven Testing
[Cest format](http://codeception.com/docs/07-AdvancedUsage#Cest-Classes) can also be used for integration testing.
In some cases it makes tests cleaner as it simplifies module access by using common `$I->` syntax:
```php
<?php
public function buildShouldHaveSequence(\UnitTester $I)
{
$build = $I->have(Build::class, ['project_id' => $this->project->id]);
$I->assertEquals(1, $build->sequence);
$build = $I->have(Build::class, ['project_id' => $this->project->id]);
$I->assertEquals(2, $build->sequence);
$this->project->refresh();
$I->assertEquals(3, $this->project->build_sequence);
}
```
This format can be recommended for testing domain and database interactions.
In Cest format you don't have native support for test doubles so it's recommended
to include a trait `\Codeception\Test\Feature\Stub` to enable mocks inside a test.
Alternatively, install and enable [Mockery module](https://github.com/Codeception/MockeryModule).
## Advanced Tools
### Specify
When writing tests you should prepare them for constant changes in your application.
Tests should be easy to read and maintain. If a specification of your application is changed,
your tests should be updated as well. If you don't have a convention inside your team for documenting tests,
you will have issues figuring out what tests will be affected by the introduction of a new feature.
That's why it's pretty important not just to cover your application with unit tests, but make unit tests self-explanatory.
We do this for scenario-driven acceptance and functional tests, and we should do this for unit and integration tests as well.
For this case we have a stand-alone project [Specify](https://github.com/Codeception/Specify)
(which is included in the phar package) for writing specifications inside unit tests:
```php
<?php
class UserTest extends \Codeception\Test\Unit
{
use \Codeception\Specify;
/** @specify */
private $user;
public function testValidation()
{
$this->user = User::create();
$this->specify("username is required", function() {
$this->user->username = null;
$this->assertFalse($this->user->validate(['username']));
});
$this->specify("username is too long", function() {
$this->user->username = 'toolooooongnaaaaaaameeee';
$this->assertFalse($this->user->validate(['username']));
});
$this->specify("username is ok", function() {
$this->user->username = 'davert';
$this->assertTrue($this->user->validate(['username']));
});
}
}
```
By using `specify` codeblocks, you can describe any piece of a test.
This makes tests much cleaner and comprehensible for everyone in your team.
Code inside `specify` blocks is isolated. In the example above, any changes to `$this->user`
will not be reflected in other code blocks as it is marked with `@specify` annotation.
Also, you may add [Codeception\Verify](https://github.com/Codeception/Verify) for BDD-style assertions.
This tiny library adds more readable assertions, which is quite nice, if you are always confused
about which argument in `assert` calls is expected and which one is actual:
```php
<?php
verify($user->getName())->equals('john');
```
### Domain Assertions
The more complicated your domain is the more explicit your tests should be. With [DomainAssert](https://github.com/Codeception/DomainAssert)
library you can easily create custom assertion methods for unit and integration tests.
It allows to reuse business rules inside assertion methods:
```php
<?php
$user = new User;
// simple custom assertions below:
$this->assertUserIsValid($user);
$this->assertUserIsAdmin($user);
// use combined explicit assertion
// to tell what you expect to check
$this->assertUserCanPostToBlog($user, $blog);
// instead of just calling a bunch of assertions
$this->assertNotNull($user);
$this->assertNotNull($blog);
$this->assertContain($user, $blog->getOwners());
```
With custom assertion methods you can improve readability of your tests and keep them focused around the specification.
### AspectMock
[AspectMock](https://github.com/Codeception/AspectMock) is an advanced mocking framework which allows you to replace any methods of any class in a test.
Static methods, class methods, date and time functions can be easily replaced with AspectMock.
For instance, you can test singletons!
```php
<?php
public function testSingleton()
{
$class = MySingleton::getInstance();
$this->assertInstanceOf('MySingleton', $class);
test::double('MySingleton', ['getInstance' => new DOMDocument]);
$this->assertInstanceOf('DOMDocument', $class);
}
```
* [AspectMock on GitHub](https://github.com/Codeception/AspectMock)
* [AspectMock in Action](http://codeception.com/07-31-2013/nothing-is-untestable-aspect-mock.html)
* [How it Works](http://codeception.com/09-13-2013/understanding-aspectmock.html)
## Conclusion
PHPUnit tests are first-class citizens in test suites. Whenever you need to write and execute unit tests,
you don't need to install PHPUnit separately, but use Codeception directly to execute them.
Some nice features can be added to common unit tests by integrating Codeception modules.
For most unit and integration testing, PHPUnit tests are enough. They run fast, and are easy to maintain.

View File

@@ -0,0 +1,522 @@
# Modules and Helpers
Codeception uses modularity to create a comfortable testing environment for every test suite you write.
All actions and assertions that can be performed by the Tester object in a class are defined in modules.
You can extend the testing suite with your own actions and assertions by writing them into a custom module.
Let's look at the following test:
```php
<?php
$I = new FunctionalTester($scenario);
$I->amOnPage('/');
$I->see('Hello');
$I->seeInDatabase('users', array('id' => 1));
$I->seeFileFound('running.lock');
```
It can operate with different entities: the web page can be loaded with the PhpBrowser module,
the database assertion uses the Db module, and file state can be checked with the Filesystem module.
Modules are attached to the Actor classes in the suite configuration.
For example, in `tests/functional.suite.yml` we should see:
```yaml
actor: FunctionalTester
modules:
enabled:
- PhpBrowser:
url: http://localhost
- Db:
dsn: "mysql:host=localhost;dbname=testdb"
- Filesystem
```
The FunctionalTester class has its methods defined in modules. Actually, it doesn't contain any of them,
but rather acts as a proxy. It knows which module executes this action and passes parameters into it.
To make your IDE see all of the FunctionalTester methods, you should run use the `codecept build` command.
It generates method signatures from enabled modules and saves them into a trait which is included in an actor.
In the current example, the `tests/support/_generated/FunctionalTesterActions.php` file will be generated.
By default, Codeception automatically rebuilds the Actions trait on each change of the suite configuration.
## Standard Modules
Codeception has many bundled modules which will help you run tests for different purposes and different environments.
The idea of modules is to share common actions, so that developers and QA engineers can concentrate on testing
and not on reinventing the wheel. Each module provides methods for testing its own part and
by combining modules you can get a powerful setup to test an application at all levels.
There is the `WebDriver` module for acceptance testing, modules for all popular PHP frameworks,
`PHPBrowser` to emulate browser execution, `REST` for testing APIs, and more.
Modules are considered to be the most valuable part of Codeception.
They are constantly improving to provide the best testing experience, and be flexible to satisfy everyone's needs.
### Module Conflicts
Modules may conflict with one another. If a module implements `Codeception\Lib\Interfaces\ConflictsWithModule`,
it might declare a conflict rule to be used with other modules. For instance, WebDriver conflicts
with all modules implementing the `Codeception\Lib\Interfaces\Web` interface.
```php
public function _conflicts()
{
return 'Codeception\Lib\Interfaces\Web';
}
```
This way if you try to use two modules sharing the same conflicted interface you will get an exception.
To avoid confusion, **Framework modules, PhpBrowser, and WebDriver** can't be used together. For instance,
the `amOnPage` method exists in all those modules, and you should not try to guess which module will actually execute it.
If you are doing acceptance testing, set up either WebDriver or PHPBrowser but do not set both up at the same time.
If you are doing functional testing, enable only one of the framework modules.
In case you need to use a module which depends on a conflicted one, specify it as a dependent module in the configuration.
You may want to use `WebDriver` with the `REST` module which interacts with a server through `PhpBrowser`.
In this case your config should look like this:
```yaml
modules:
enabled:
- WebDriver:
browser: firefox
url: http://localhost
- REST:
url: http://localhost/api/v1
depends: PhpBrowser
```
This configuration will allow you to send GET/POST requests to the server's APIs while working with a site through a browser.
If you only need some parts of a conflicted module to be loaded, refer to the next section.
### Module Parts
Modules with *Parts* section in their reference can be partially loaded. This way, the `$I` object will have actions
belonging to only a specific part of that module. Partially loaded modules can be also used to avoid module conflicts.
For instance, the Laravel5 module has an ORM part which contains database actions. You can enable the PhpBrowser module
for testing and Laravel + ORM for connecting to the database and checking the data.
```yaml
modules:
enabled:
- PhpBrowser:
url: http://localhost
- Laravel5:
part: ORM
```
The modules won't conflict as actions with the same names won't be loaded.
The REST module has parts for `Xml` and `Json` in the same way. If you are testing a REST service with only JSON responses,
you can enable just the JSON part of this module:
```yaml
actor: ApiTester
modules:
enabled:
- REST:
url: http://serviceapp/api/v1/
depends: PhpBrowser
part: Json
```
## Helpers
Codeception doesn't restrict you to only the modules from the main repository.
Your project might need your own actions added to the test suite. By running the `bootstrap` command,
Codeception generates three dummy modules for you, one for each of the newly created suites.
These custom modules are called 'Helpers', and they can be found in the `tests/_support` directory.
```php
<?php
namespace Helper;
// here you can define custom functions for FunctionalTester
class Functional extends \Codeception\Module
{
}
```
Actions are also quite simple. Every action you define is a public function. Write a public method,
then run the `build` command, and you will see the new function added into the FunctionalTester class.
<div class="alert alert-info">
Public methods prefixed by `_` are treated as hidden and won't be added to your Actor class.
</div>
Assertions can be a bit tricky. First of all, it's recommended to prefix all your assertion actions with `see` or `dontSee`.
Name your assertions like this:
```php
<?php
$I->seePageReloaded();
$I->seeClassIsLoaded($classname);
$I->dontSeeUserExist($user);
```
And then use them in your tests:
```php
<?php
$I->seePageReloaded();
$I->seeClassIsLoaded('FunctionalTester');
$I->dontSeeUserExist($user);
```
You can define assertions by using assertXXX methods in your modules.
```php
<?php
function seeClassExist($class)
{
$this->assertTrue(class_exists($class));
}
```
In your helpers you can use these assertions:
```php
<?php
function seeCanCheckEverything($thing)
{
$this->assertTrue(isset($thing), "this thing is set");
$this->assertFalse(empty($any), "this thing is not empty");
$this->assertNotNull($thing, "this thing is not null");
$this->assertContains("world", $thing, "this thing contains 'world'");
$this->assertNotContains("bye", $thing, "this thing doesn't contain 'bye'");
$this->assertEquals("hello world", $thing, "this thing is 'Hello world'!");
// ...
}
```
### Accessing Other Modules
It's possible that you will need to access internal data or functions from other modules.
For example, for your module you might need to access the responses or internal actions of other modules.
Modules can interact with each other through the `getModule` method.
Please note that this method will throw an exception if the required module was not loaded.
Let's imagine that we are writing a module that reconnects to a database.
It's supposed to use the dbh connection value from the Db module.
```php
<?php
function reconnectToDatabase() {
$dbh = $this->getModule('Db')->dbh;
$dbh->close();
$dbh->open();
}
```
By using the `getModule` function, you get access to all of the public methods and properties of the requested module.
The `dbh` property was defined as public specifically to be available to other modules.
Modules may also contain methods that are exposed for use in helper classes. Those methods start with a `_` prefix
and are not available in Actor classes, so can be accessed only from modules and extensions.
You should use them to write your own actions using module internals.
```php
<?php
function seeNumResults($num)
{
// retrieving webdriver session
/**@var $table \Facebook\WebDriver\WebDriverElement */
$elements = $this->getModule('WebDriver')->_findElements('#result');
$this->assertNotEmpty($elements);
$table = reset($elements);
$this->assertEquals('table', $table->getTagName());
$results = $table->findElements('tr');
// asserting that table contains exactly $num rows
$this->assertEquals($num, count($results));
}
```
In this example we use the API of the <a href="https://github.com/facebook/php-webdriver">facebook/php-webdriver</a> library,
a Selenium WebDriver client the module is build on.
You can also access the `webDriver` property of a module to get access to the `Facebook\WebDriver\RemoteWebDriver` instance
for direct Selenium interaction.
### Extending a Module
If accessing modules doesn't provide enough flexibility, you can extend a module inside a Helper class:
```php
<?php
namespace Helper;
class MyExtendedSelenium extends \Codeception\Module\WebDriver {
}
```
In this helper you can replace the parent's methods with your own implementation.
You can also replace the `_before` and `_after` hooks, which might be an option
when you need to customize starting and stopping of a testing session.
### Hooks
Each module can handle events from the running test. A module can be executed before the test starts,
or after the test is finished. This can be useful for bootstrap/cleanup actions.
You can also define special behavior for when the test fails. This may help you in debugging the issue.
For example, the PhpBrowser module saves the current webpage to the `tests/_output` directory when a test fails.
All hooks are defined in [Codeception\Module](http://codeception.com/docs/reference/Commands) and are listed here. You are free to redefine them in your module.
```php
<?php
// HOOK: used after configuration is loaded
public function _initialize()
{
}
// HOOK: before each suite
public function _beforeSuite($settings = array())
{
}
// HOOK: after suite
public function _afterSuite()
{
}
// HOOK: before each step
public function _beforeStep(\Codeception\Step $step)
{
}
// HOOK: after each step
public function _afterStep(\Codeception\Step $step)
{
}
// HOOK: before test
public function _before(\Codeception\TestInterface $test)
{
}
// HOOK: after test
public function _after(\Codeception\TestInterface $test)
{
}
// HOOK: on fail
public function _failed(\Codeception\TestInterface $test, $fail)
{
}
```
Please note that methods with a `_` prefix are not added to the Actor class.
This allows them to be defined as public but used only for internal purposes.
### Debug
As we mentioned, the `_failed` hook can help in debugging a failed test.
You have the opportunity to save the current test's state and show it to the user, but you are not limited to this.
Each module can output internal values that may be useful during debug.
For example, the PhpBrowser module prints the response code and current URL every time it moves to a new page.
Thus, modules are not black boxes. They are trying to show you what is happening during the test.
This makes debugging your tests less painful.
To display additional information, use the `debug` and `debugSection` methods of the module.
Here is an example of how it works for PhpBrowser:
```php
<?php
$this->debugSection('Request', $params);
$this->client->request($method, $uri, $params);
$this->debug('Response Code: ' . $this->client->getStatusCode());
```
This test, running with the PhpBrowser module in debug mode, will print something like this:
```bash
I click "All pages"
* Request (GET) http://localhost/pages {}
* Response code: 200
```
## Configuration
Modules and Helpers can be configured from the suite configuration file, or globally from `codeception.yml`.
Mandatory parameters should be defined in the `$requiredFields` property of the class.
Here is how it is done in the Db module:
```php
<?php
class Db extends \Codeception\Module
{
protected $requiredFields = ['dsn', 'user', 'password'];
// ...
}
```
The next time you start the suite without setting one of these values, an exception will be thrown.
For optional parameters, you should set default values. The `$config` property is used to define optional parameters
as well as their values. In the WebDriver module we use the default Selenium Server address and port.
```php
<?php
class WebDriver extends \Codeception\Module
{
protected $requiredFields = ['browser', 'url'];
protected $config = ['host' => '127.0.0.1', 'port' => '4444'];
// ...
}
```
The host and port parameter can be redefined in the suite configuration.
Values are set in the `modules:config` section of the configuration file.
```yaml
modules:
enabled:
- WebDriver:
url: 'http://mysite.com/'
browser: 'firefox'
- Db:
cleanup: false
repopulate: false
```
Optional and mandatory parameters can be accessed through the `$config` property.
Use `$this->config['parameter']` to get its value.
### Dynamic Configuration With Parameters
Modules can be dynamically configured from environment variables.
Parameter storage should be specified in the global `codeception.yml` configuration inside the `params` section.
Parameters can be loaded from environment vars, from yaml (Symfony format), .env (Laravel format), ini, or php files.
Use the `params` section of the global configuration file `codeception.yml` to specify how to load them.
You can specify several sources for parameters to be loaded from.
Example: load parameters from the environment:
```yaml
params:
- env # load params from environment vars
```
Example: load parameters from YAML file (Symfony):
```yaml
params:
- app/config/parameters.yml
```
Example: load parameters from php file (Yii)
```yaml
params:
- config/params.php
```
Example: load parameters from .env files (Laravel):
```yaml
params:
- .env
- .env.testing
```
Once loaded, parameter variables can be used as module configuration values.
Use a variable name wrapped with `%` as a placeholder and it will be replaced by its value.
Let's say we want to specify credentials for a cloud testing service. We have loaded `SAUCE_USER`
and `SAUCE_KEY` variables from environment, and now we are passing their values into config of `WebDriver`:
```yaml
modules:
enabled:
- WebDriver:
url: http://mysite.com
host: '%SAUCE_USER%:%SAUCE_KEY%@ondemand.saucelabs.com'
```
Parameters are also useful to provide connection credentials for the `Db` module (taken from Laravel's .env files):
```yaml
module:
enabled:
- Db:
dsn: "mysql:host=%DB_HOST%;dbname=%DB_DATABASE%"
user: "%DB_USERNAME%"
password: "DB_PASSWORD"
```
### Runtime Configuration
If you want to reconfigure a module at runtime, you can use the `_reconfigure` method of the module.
You may call it from a helper class and pass in all the fields you want to change.
In this case configuration will be changed instantly. In next example we change root URL for PhpBrowser to point to the admin area,
so next `amOnPage('/')` will open `/admin/` page.
```php
<?php
$this->getModule('PhpBrowser')->_reconfigure(array('url' => 'http://localhost/admin'));
```
However, in WebDriver configuration changes can't be applied that easily. For instance, if you change the browser you need to close the current browser session and start a new one.
For that WebDriver module provides `_restart` method which takes an array with config and restarts the browser.
```php
<?php
// start chrome
$this->getModule('WebDriver')->_restart(['browser' => 'chrome']);
// or just restart browser
$this->getModule('WebDriver')->_restart();
```
At the end of a test all configuration changes will be rolled back to the original configuration values.
### Runtime Configuration of a Test
Sometimes it is needed to set custom configuration for a specific test only.
For [Cest](http://codeception.com/docs/07-AdvancedUsage#Cest-Classes) and [Test\Unit](http://codeception.com/docs/05-UnitTests)
formats you can use `@prepare` annotation which can execute the code before other hooks are executed. This allows `@prepare`
to change the module configuration in runtime. `@prepare` uses [dependency injection](http://codeception.com/docs/07-AdvancedUsage#Dependency-Injection)
to automatically inject required modules into a method.
To run a specific test only in Chrome browser, you can call `_reconfigure` from WebDriver module for a test itself using `@prepare`.
```php
<?php
/**
* @prepare useChrome
*/
public function chromeSpecificTest()
{
// ...
}
protected function useChrome(\Codeception\Module\WebDriver $webdriver)
{
// WebDriver was injected by the class name
$webdriver->_reconfigure(['browser' => 'chrome']);
}
```
Prepare methods can invoke all methods of a module, as well as hidden API methods (starting with `_`). Use them to customize the module setup for a specific test.
To change module configuration for a specific group of tests use [GroupObjects](http://codeception.com/docs/08-Customization#Group-Objects).
## Conclusion
Modules are the real power of Codeception. They are used to emulate multiple inheritances for Actor classes
(UnitTester, FunctionalTester, AcceptanceTester, etc). Codeception provides modules to emulate web requests,
access data, interact with popular PHP libraries, etc. If the bundled modules are not enough for you that's OK,
you are free to write your own! Use Helpers (custom modules) for everything that Codeception can't do out of the box.
Helpers also can be used to extend the functionality of the original modules.

View File

@@ -0,0 +1,370 @@
# Reusing Test Code
Codeception uses modularity to create a comfortable testing environment for every test suite you write.
Modules allow you to choose the actions and assertions that can be performed in tests.
## What are Actors
All actions and assertions that can be performed by the Actor object in a class are defined in modules.
It might look like Codeception limits you in testing, but that's not true. You can extend the testing suite
with your own actions and assertions, by writing them into a custom module, called a Helper.
We will get back to this later in this chapter, but for now let's look at the following test:
```php
<?php
$I->amOnPage('/');
$I->see('Hello');
$I->seeInDatabase('users', ['id' => 1]);
$I->seeFileFound('running.lock');
```
It can operate with different entities: the web page can be loaded with the PhpBrowser module,
the database assertion uses the Db module, and the file state can be checked with the Filesystem module.
Modules are attached to Actor classes in the suite config.
For example, in `tests/acceptance.suite.yml` we should see:
```yaml
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: http://localhost
- Db
- Filesystem
```
The AcceptanceTester class has its methods defined in modules.
Let's see what's inside the `AcceptanceTester` class, which is located inside the `tests/_support` directory:
```php
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void haveFriend($name, $actorClass = null)
*
* @SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
}
```
The most important part is the `_generated\AcceptanceTesterActions` trait, which is used as a proxy for enabled modules.
It knows which module executes which action and passes parameters into it.
This trait was created by running `codecept build` and is regenerated each time module or configuration changes.
### Authorization
It is recommended to put widely used actions inside an Actor class. A good example is the `login` action
which would probably be actively involved in acceptance or functional testing:
``` php
<?php
class AcceptanceTester extends \Codeception\Actor
{
// do not ever remove this line!
use _generated\AcceptanceTesterActions;
public function login($name, $password)
{
$I = $this;
$I->amOnPage('/login');
$I->submitForm('#loginForm', [
'login' => $name,
'password' => $password
]);
$I->see($name, '.navbar');
}
}
```
Now you can use the `login` method inside your tests:
```php
<?php
$I = new AcceptanceTester($scenario);
$I->login('miles', '123456');
```
However, implementing all actions for reuse in a single actor class may lead to
breaking the [Single Responsibility Principle](http://en.wikipedia.org/wiki/Single_responsibility_principle).
### Session Snapshot
If you need to authorize a user for each test, you can do so by submitting the login form at the beginning of every test.
Running those steps takes time, and in the case of Selenium tests (which are slow by themselves)
that time loss can become significant.
Codeception allows you to share cookies between tests, so a test user can stay logged in for other tests.
Let's improve the code of our `login` method, executing the form submission only once
and restoring the session from cookies for each subsequent login function call:
``` php
<?php
public function login($name, $password)
{
$I = $this;
// if snapshot exists - skipping login
if ($I->loadSessionSnapshot('login')) {
return;
}
// logging in
$I->amOnPage('/login');
$I->submitForm('#loginForm', [
'login' => $name,
'password' => $password
]);
$I->see($name, '.navbar');
// saving snapshot
$I->saveSessionSnapshot('login');
}
```
Note that session restoration only works for `WebDriver` modules
(modules implementing `Codeception\Lib\Interfaces\SessionSnapshot`).
## StepObjects
StepObjects are great if you need some common functionality for a group of tests.
Let's say you are going to test an admin area of a site. You probably won't need the same actions from the admin area
while testing the front end, so it's a good idea to move these admin-specific tests into their own class.
We call such a classes StepObjects.
Lets create an Admin StepObject with the generator:
```bash
php vendor/bin/codecept generate:stepobject acceptance Admin
```
You can supply optional action names. Enter one at a time, followed by a newline.
End with an empty line to continue to StepObject creation.
```bash
php vendor/bin/codecept generate:stepobject acceptance Admin
Add action to StepObject class (ENTER to exit): loginAsAdmin
Add action to StepObject class (ENTER to exit):
StepObject was created in /tests/acceptance/_support/Step/Acceptance/Admin.php
```
This will generate a class in `/tests/_support/Step/Acceptance/Admin.php` similar to this:
```php
<?php
namespace Step\Acceptance;
class Admin extends \AcceptanceTester
{
public function loginAsAdmin()
{
$I = $this;
}
}
```
As you see, this class is very simple. It extends the `AcceptanceTester` class,
meaning it can access all the methods and properties of `AcceptanceTester`.
The `loginAsAdmin` method may be implemented like this:
```php
<?php
namespace Step\Acceptance;
class Admin extends \AcceptanceTester
{
public function loginAsAdmin()
{
$I = $this;
$I->amOnPage('/admin');
$I->fillField('username', 'admin');
$I->fillField('password', '123456');
$I->click('Login');
}
}
```
In tests, you can use a StepObject by instantiating `Step\Acceptance\Admin` instead of `AcceptanceTester`:
```php
<?php
use Step\Acceptance\Admin as AdminTester;
$I = new AdminTester($scenario);
$I->loginAsAdmin();
```
The same way as above, a StepObject can be instantiated automatically by the Dependency Injection Container
when used inside the Cest format:
```php
<?php
class UserCest
{
function showUserProfile(\Step\Acceptance\Admin $I)
{
$I->loginAsAdmin();
$I->amOnPage('/admin/profile');
$I->see('Admin Profile', 'h1');
}
}
```
If you have a complex interaction scenario, you may use several step objects in one test.
If you feel like adding too many actions into your Actor class
(which is AcceptanceTester in this case) consider moving some of them into separate StepObjects.
## PageObjects
For acceptance and functional testing, we will not only need to have common actions being reused across different tests,
we should have buttons, links and form fields being reused as well. For those cases we need to implement
the [PageObject pattern](http://docs.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern),
which is widely used by test automation engineers. The PageObject pattern represents a web page as a class
and the DOM elements on that page as its properties, and some basic interactions as its methods.
PageObjects are very important when you are developing a flexible architecture of your tests.
Do not hard-code complex CSS or XPath locators in your tests but rather move them into PageObject classes.
Codeception can generate a PageObject class for you with command:
```bash
php vendor/bin/codecept generate:pageobject Login
```
This will create a `Login` class in `tests/_support/Page`.
The basic PageObject is nothing more than an empty class with a few stubs.
It is expected that you will populate it with the UI locators of a page it represents
and then those locators will be used on a page.
Locators are represented with public static properties:
```php
<?php
namespace Page;
class Login
{
public static $URL = '/login';
public static $usernameField = '#mainForm #username';
public static $passwordField = '#mainForm input[name=password]';
public static $loginButton = '#mainForm input[type=submit]';
}
```
And this is how this page object can be used in a test:
```php
<?php
use Page\Login as LoginPage;
$I = new AcceptanceTester($scenario);
$I->wantTo('login to site');
$I->amOnPage(LoginPage::$URL);
$I->fillField(LoginPage::$usernameField, 'bill evans');
$I->fillField(LoginPage::$passwordField, 'debby');
$I->click(LoginPage::$loginButton);
$I->see('Welcome, bill');
```
As you see, you can freely change markup of your login page, and all the tests interacting with this page
will have their locators updated according to properties of LoginPage class.
But let's move further. The PageObject concept specifies that the methods for the page interaction
should also be stored in a PageObject class. It now stores a passed instance of an Actor class.
An AcceptanceTester can be accessed via the `AcceptanceTester` property of that class.
Let's define a `login` method in this class:
```php
<?php
namespace Page;
class Login
{
public static $URL = '/login';
public static $usernameField = '#mainForm #username';
public static $passwordField = '#mainForm input[name=password]';
public static $loginButton = '#mainForm input[type=submit]';
/**
* @var AcceptanceTester
*/
protected $tester;
public function __construct(\AcceptanceTester $I)
{
$this->tester = $I;
}
public function login($name, $password)
{
$I = $this->tester;
$I->amOnPage(self::$URL);
$I->fillField(self::$usernameField, $name);
$I->fillField(self::$passwordField, $password);
$I->click(self::$loginButton);
return $this;
}
}
```
And here is an example of how this PageObject can be used in a test:
```php
<?php
use Page\Login as LoginPage;
$I = new AcceptanceTester($scenario);
$loginPage = new LoginPage($I);
$loginPage->login('bill evans', 'debby');
$I->amOnPage('/profile');
$I->see('Bill Evans Profile', 'h1');
```
If you write your scenario-driven tests in the Cest format (which is the recommended approach),
you can bypass the manual creation of a PageObject and delegate this task to Codeception.
If you specify which object you need for a test, Codeception will try to create it using the dependency injection container.
In the case of a PageObject you should declare a class as a parameter for a test method:
```php
<?php
class UserCest
{
function showUserProfile(AcceptanceTester $I, \Page\Login $loginPage)
{
$loginPage->login('bill evans', 'debby');
$I->amOnPage('/profile');
$I->see('Bill Evans Profile', 'h1');
}
}
```
The dependency injection container can construct any object that requires any known class type.
For instance, `Page\Login` required `AcceptanceTester`, and so it was injected into `Page\Login` constructor,
and PageObject was created and passed into method arguments. You should explicitly specify
the types of required objects for Codeception to know what objects should be created for a test.
Dependency Injection will be described in the next chapter.
## Conclusion
There are lots of ways to create reusable and readable tests. Group common actions together
and move them to an Actor class or StepObjects. Move CSS and XPath locators into PageObjects.
Write your custom actions and assertions in Helpers.
Scenario-driven tests should not contain anything more complex than `$I->doSomething` commands.
Following this approach will allow you to keep your tests clean, readable, stable and make them easy to maintain.

View File

@@ -0,0 +1,739 @@
# Advanced Usage
In this chapter we will cover some techniques and options that you can use to improve your testing experience
and keep your project better organized.
## Cest Classes
If you want to get a class-like structure for your Cepts, you can use the Cest format instead of plain PHP.
It is very simple and is fully compatible with Cept scenarios. It means that if you feel that your test is long enough
and you want to split it, you can easily move it into classes.
You can create a Cest file by running the command:
```bash
$ php vendor/bin/codecept generate:cest suitename CestName
```
The generated file will look like this:
```php
<?php
class BasicCest
{
public function _before(\AcceptanceTester $I)
{
}
public function _after(\AcceptanceTester $I)
{
}
// tests
public function tryToTest(\AcceptanceTester $I)
{
}
}
```
**Each public method of Cest (except those starting with `_`) will be executed as a test**
and will receive an instance of the Actor class as the first parameter and the `$scenario` variable as the second one.
In `_before` and `_after` methods you can use common setups and teardowns for the tests in the class.
As you see, we are passing the Actor object into `tryToTest` method. This allows us to write scenarios the way we did before:
```php
<?php
class BasicCest
{
// test
public function tryToTest(\AcceptanceTester $I)
{
$I->amOnPage('/');
$I->click('Login');
$I->fillField('username', 'john');
$I->fillField('password', 'coltrane');
$I->click('Enter');
$I->see('Hello, John');
$I->seeInCurrentUrl('/account');
}
}
```
As you see, Cest classes have no parents.
This is done intentionally. It allows you to extend your classes with common behaviors and workarounds
that may be used in child classes. But don't forget to make these methods `protected` so they won't be executed as tests.
Cest format also can contain hooks based on test results:
* `_failed` will be executed on failed test
* `_passed` will be executed on passed test
```php
<?php
public function _failed(\AcceptanceTester $I)
{
// will be executed on test failure
}
public function _passed(\AcceptanceTester $I)
{
// will be executed when test is successful
}
```
## Dependency Injection
Codeception supports simple dependency injection for Cest and \Codeception\TestCase\Test classes.
It means that you can specify which classes you need as parameters of the special `_inject()` method,
and Codeception will automatically create the respective objects and invoke this method,
passing all dependencies as arguments. This may be useful when working with Helpers. Here's an example for Cest:
```php
<?php
class SignUpCest
{
/**
* @var Helper\SignUp
*/
protected $signUp;
/**
* @var Helper\NavBarHelper
*/
protected $navBar;
protected function _inject(\Helper\SignUp $signUp, \Helper\NavBar $navBar)
{
$this->signUp = $signUp;
$this->navBar = $navBar;
}
public function signUp(\AcceptanceTester $I)
{
$this->navBar->click('Sign up');
$this->signUp->register([
'first_name' => 'Joe',
'last_name' => 'Jones',
'email' => 'joe@jones.com',
'password' => '1234',
'password_confirmation' => '1234'
]);
}
}
```
And for Test classes:
```php
<?php
class MathTest extends \Codeception\TestCase\Test
{
/**
* @var \UnitTester
*/
protected $tester;
/**
* @var Helper\Math
*/
protected $math;
protected function _inject(\Helper\Math $math)
{
$this->math = $math;
}
public function testAll()
{
$this->assertEquals(3, $this->math->add(1, 2));
$this->assertEquals(1, $this->math->subtract(3, 2));
}
}
```
However, Dependency Injection is not limited to this. It allows you to **inject any class**,
which can be constructed with arguments known to Codeception.
In order to make auto-wiring work, you will need to implement the `_inject()` method with the list of desired arguments.
It is important to specify the type of arguments, so Codeception can guess which objects are expected to be received.
The `_inject()` will only be invoked once, just after creation of the TestCase object (either Cest or Test).
Dependency Injection will also work in a similar manner for Helper and Actor classes.
Each test of a Cest class can declare its own dependencies and receive them from method arguments:
```php
<?php
class UserCest
{
function updateUser(\Helper\User $u, \AcceptanceTester $I, \Page\User $userPage)
{
$user = $u->createDummyUser();
$userPage->login($user->getName(), $user->getPassword());
$userPage->updateProfile(['name' => 'Bill']);
$I->see('Profile was saved');
$I->see('Profile of Bill','h1');
}
}
```
Moreover, Codeception can resolve dependencies recursively (when `A` depends on `B`, and `B` depends on `C` etc.)
and handle parameters of primitive types with default values (like `$param = 'default'`).
Of course, you are not allowed to have *cyclic dependencies*.
## Example Annotation
What if you want to execute the same test scenario with different data? In this case you can inject examples
as `\Codeception\Example` instances.
Data is defined via the `@example` annotation, using JSON or Doctrine-style notation (limited to a single line). Doctrine-style:
```php
<?php
class EndpointCest
{
/**
* @example ["/api/", 200]
* @example ["/api/protected", 401]
* @example ["/api/not-found-url", 404]
* @example ["/api/faulty", 500]
*/
public function checkEndpoints(ApiTester $I, \Codeception\Example $example)
{
$I->sendGET($example[0]);
$I->seeResponseCodeIs($example[1]);
}
}
```
JSON:
```php
<?php
class PageCest
{
/**
* @example { "url": "/", "title": "Welcome" }
* @example { "url": "/info", "title": "Info" }
* @example { "url": "/about", "title": "About Us" }
* @example { "url": "/contact", "title": "Contact Us" }
*/
public function staticPages(AcceptanceTester $I, \Codeception\Example $example)
{
$I->amOnPage($example['url']);
$I->see($example['title'], 'h1');
$I->seeInTitle($example['title']);
}
}
```
<div class="alert alert-info">
If you use JSON notation please keep in mind that all string keys
and values should be enclosed in double quotes (`"`) according to JSON standard.
</div>
Key-value data in Doctrine-style annotation syntax:
```php
<?php
class PageCest
{
/**
* @example(url="/", title="Welcome")
* @example(url="/info", title="Info")
* @example(url="/about", title="About Us")
* @example(url="/contact", title="Contact Us")
*/
public function staticPages(AcceptanceTester $I, \Codeception\Example $example)
{
$I->amOnPage($example['url']);
$I->see($example['title'], 'h1');
$I->seeInTitle($example['title']);
}
}
```
## DataProvider Annotations
You can also use the `@dataProvider` annotation for creating dynamic examples for [Cest classes](#Cest-Classes), using a **protected method** for providing example data:
```php
<?php
class PageCest
{
/**
* @dataProvider pageProvider
*/
public function staticPages(AcceptanceTester $I, \Codeception\Example $example)
{
$I->amOnPage($example['url']);
$I->see($example['title'], 'h1');
$I->seeInTitle($example['title']);
}
/**
* @return array
*/
protected function pageProvider() // alternatively, if you want the function to be public, be sure to prefix it with `_`
{
return [
['url'=>"/", 'title'=>"Welcome"],
['url'=>"/info", 'title'=>"Info"],
['url'=>"/about", 'title'=>"About Us"],
['url'=>"/contact", 'title'=>"Contact Us"]
];
}
}
```
`@dataprovider` annotation is also available for [unit tests](https://codeception.com/docs/05-UnitTests), in this case the data provider **method must be public**.
For more details about how to use data provider for unit tests, please refer to [PHPUnit documentation](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers).
## Before/After Annotations
You can control execution flow with `@before` and `@after` annotations. You may move common actions
into protected (non-test) methods and invoke them before or after the test method by putting them into annotations.
It is possible to invoke several methods by using more than one `@before` or `@after` annotation.
Methods are invoked in order from top to bottom.
```php
<?php
class ModeratorCest {
protected function login(AcceptanceTester $I)
{
$I->amOnPage('/login');
$I->fillField('Username', 'miles');
$I->fillField('Password', 'davis');
$I->click('Login');
}
/**
* @before login
*/
public function banUser(AcceptanceTester $I)
{
$I->amOnPage('/users/charlie-parker');
$I->see('Ban', '.button');
$I->click('Ban');
}
/**
* @before login
* @before cleanup
* @after logout
* @after close
*/
public function addUser(AcceptanceTester $I)
{
$I->amOnPage('/users/charlie-parker');
$I->see('Ban', '.button');
$I->click('Ban');
}
}
```
## Environments
For cases where you need to run tests with different configurations you can define different config environments.
The most typical use cases are running acceptance tests in different browsers,
or running database tests using different database engines.
Let's demonstrate the usage of environments for the browsers case.
We need to add some new lines to `acceptance.suite.yml`:
``` yaml
actor: AcceptanceTester
modules:
enabled:
- WebDriver
- \Helper\Acceptance
config:
WebDriver:
url: 'http://127.0.0.1:8000/'
browser: 'firefox'
env:
phantom:
modules:
config:
WebDriver:
browser: 'phantomjs'
chrome:
modules:
config:
WebDriver:
browser: 'chrome'
firefox:
# nothing changed
```
Basically you can define different environments inside the `env` root, name them (`phantom`, `chrome` etc.),
and then redefine any configuration parameters that were set before.
You can also define environments in separate configuration files placed in the directory specified by the `envs` option in
the `paths` configuration:
```yaml
paths:
envs: tests/_envs
```
The names of these files are used as environments names
(e.g. `chrome.yml` or `chrome.dist.yml` for an environment named `chrome`).
You can generate a new file with this environment configuration by using the `generate:environment` command:
```bash
$ php vendor/bin/codecept g:env chrome
```
In that file you can specify just the options you wish to override:
```yaml
modules:
config:
WebDriver:
browser: 'chrome'
```
The environment configuration files are merged into the main configuration before the suite configuration is merged.
You can easily switch between those configs by running tests with `--env` option.
To run the tests only for PhantomJS you just need to pass `--env phantom` as an option:
```bash
$ php vendor/bin/codecept run acceptance --env phantom
```
To run the tests in all 3 browsers, list all the environments:
```bash
$ php vendor/bin/codecept run acceptance --env phantom --env chrome --env firefox
```
The tests will be executed 3 times, each time in a different browser.
It's also possible to merge multiple environments into a single configuration by separating them with a comma:
```bash
$ php vendor/bin/codecept run acceptance --env dev,phantom --env dev,chrome --env dev,firefox
```
The configuration is merged in the order given.
This way you can easily create multiple combinations of your environment configurations.
Depending on the environment, you may choose which tests are to be executed.
For example, you might need some tests to be executed in Firefox only, and some tests in Chrome only.
The desired environments can be specified with the `@env` annotation for tests in Test and Cest formats:
```php
<?php
class UserCest
{
/**
* This test will be executed only in 'firefox' and 'phantom' environments
*
* @env firefox
* @env phantom
*/
public function webkitOnlyTest(AcceptanceTester $I)
{
// I do something
}
}
```
For Cept you should use simple comments:
```php
<?php
// @env firefox
// @env phantom
```
This way you can easily control which tests will be executed for each environment.
### Current values
Sometimes you may need to change the test behavior in real time.
For instance, the behavior of the same test may differ in Firefox and in Chrome.
In runtime we can retrieve the current environment name, test name,
or list of enabled modules by calling the `$scenario->current()` method.
```php
<?php
// retrieve current environment
$scenario->current('env');
// list of all enabled modules
$scenario->current('modules');
// test name
$scenario->current('name');
// browser name (if WebDriver module enabled)
$scenario->current('browser');
// capabilities (if WebDriver module enabled)
$scenario->current('capabilities');
```
You can access `\Codeception\Scenario` in the Cept and Cest formats.
In Cept, the `$scenario` variable is available by default,
while in Cest you should retrieve it through dependency injection:
```php
<?php
public function myTest(\AcceptanceTester $I, \Codeception\Scenario $scenario)
{
if ($scenario->current('browser') == 'phantomjs') {
// emulate popups for PhantomJS
$I->executeScript('window.alert = function(){return true;}');
}
}
```
`Codeception\Scenario` is also available in Actor classes and StepObjects. You can access it with `$this->getScenario()`.
### Dependencies
With the `@depends` annotation you can specify a test that should be passed before the current one.
If that test fails, the current test will be skipped. You should pass the method name of the test you are relying on.
```php
<?php
class ModeratorCest {
public function login(AcceptanceTester $I)
{
// logs moderator in
}
/**
* @depends login
*/
public function banUser(AcceptanceTester $I)
{
// bans user
}
}
```
`@depends` applies to the `Cest` and `Codeception\Test\Unit` formats. Dependencies can be set across different classes.
To specify a dependent test from another file you should provide a *test signature*.
Normally, the test signature matches the `className:methodName` format.
But to get the exact test signature just run the test with the `--steps` option to see it:
```
Signature: ModeratorCest:login`
```
Codeception reorders tests so dependent tests will always be executed before the tests that rely on them.
## Interactive Console
The interactive console was added to try Codeception commands before executing them inside a test.
![console](http://codeception.com/images/console.png)
You can run the console with the following command:
``` bash
$ php vendor/bin/codecept console suitename
```
Now you can execute all the commands of an appropriate Actor class and see the results immediately.
This is especially useful when used with the `WebDriver` module. It always takes too long to launch Selenium
and the browser for tests. But with the console you can try different selectors, and different commands,
and then write a test that should pass when executed.
And a special hint: show your boss how you can easily manipulate web pages with the console and Selenium.
It will be easy to convince them to automate this step and introduce acceptance testing to the project.
## Running from different folders
If you have several projects with Codeception tests, you can use a single `codecept` file to run all of your tests.
You can pass the `-c` option to any Codeception command (except `bootstrap`), to execute Codeception in another directory:
```bash
$ php vendor/bin/codecept run -c ~/projects/ecommerce/
$ php vendor/bin/codecept run -c ~/projects/drupal/
$ php vendor/bin/codecept generate:cept acceptance CreateArticle -c ~/projects/drupal/
```
To create a project in directory different from the current one, just provide its path as a parameter:
```bash
$ php vendor/bin/codecept bootstrap ~/projects/drupal/
```
Also, the `-c` option allows you to specify another config file to be used.
Thus, you can have several `codeception.yml` files for your test suite (e.g. to to specify different environments
and settings). Just pass the `.yml` filename as the `-c` parameter to execute tests with specific config settings.
## Groups
There are several ways to execute a bunch of tests. You can run tests from a specific directory:
```bash
$ php vendor/bin/codecept run tests/acceptance/admin
```
You can execute one (or several) specific groups of tests:
```bash
$ php vendor/bin/codecept run -g admin -g editor
```
The concept of groups was taken from PHPUnit and behave in the same way.
For Test and Cest files you can use the `@group` annotation to add a test to a group.
```php
<?php
/**
* @group admin
*/
public function testAdminUser()
{
}
```
For Cept files, use pseudo-annotations in comments:
```php
<?php
// @group admin
// @group editor
$I = new AcceptanceTester($scenario);
$I->wantToTest('admin area');
```
For `.feature`-files (Gherkin) use tags:
```gherkin
@admin @editor
Feature: Admin area
```
### Group Files
Groups can be defined in global or suite configuration files.
Tests for groups can be specified as an array of file names or directories containing them:
```yaml
groups:
# add 2 tests to db group
db: [tests/unit/PersistTest.php, tests/unit/DataTest.php]
# add all tests from a directory to api group
api: [tests/functional/api]
```
A list of tests for the group can be passed from a Group file. It should be defined in plain text with test names on separate lines:
```bash
tests/unit/DbTest.php
tests/unit/UserTest.php:create
tests/unit/UserTest.php:update
```
A group file can be included by its relative filename:
```yaml
groups:
# requiring a group file
slow: tests/_data/slow.txt
```
You can create group files manually or generate them from third party applications.
For example, you can write a script that updates the slow group by taking the slowest tests from xml report.
You can even specify patterns for loading multiple group files with a single definition:
```yaml
groups:
p*: tests/_data/p*
```
This will load all found `p*` files in `tests/_data` as groups. Group names will be as follows p1,p2,...,pN.
## Formats
In addition to the standard test formats (Cept, Cest, Unit, Gherkin) you can implement your own format classes to customise your test execution.
Specify these in your suite configuration:
```yaml
formats:
- \My\Namespace\MyFormat
```
Then define a class which implements the LoaderInterface
```php
namespace My\Namespace;
class MyFormat implements \Codeception\Test\Loader\LoaderInterface
{
protected $tests;
protected $settings;
public function __construct($settings = [])
{
//These are the suite settings
$this->settings = $settings;
}
public function loadTests($filename)
{
//Load file and create tests
}
public function getTests()
{
return $this->tests;
}
public function getPattern()
{
return '~Myformat\.php$~';
}
}
```
## Shell auto-completion
For bash and zsh shells, you can use auto-completion for your Codeception projects by executing the following in your shell (or add it to your .bashrc/.zshrc):
```bash
# BASH ~4.x, ZSH
source <([codecept location] _completion --generate-hook --program codecept --use-vendor-bin)
# BASH ~3.x, ZSH
[codecept location] _completion --generate-hook --program codecept --use-vendor-bin | source /dev/stdin
# BASH (any version)
eval $([codecept location] _completion --generate-hook --program codecept --use-vendor-bin)
```
### Explanation
By using the above code in your shell, Codeception will try to autocomplete the following:
* Commands
* Suites
* Test paths
Usage of `-use-vendor-bin` is optional. This option will work for most Codeception projects, where Codeception is located in your `vendor/bin` folder.
But in case you are using a global Codeception installation for example, you wouldn't use this option.
Note that with the `-use-vendor-bin` option, your commands will be completed using the Codeception binary located in your project's root.
Without the option, it will use whatever Codeception binary you originally used to generate the completion script ('codecept location' in the above examples)
## Conclusion
Codeception is a framework which may look simple at first glance
but it allows you to build powerful tests with a single API, refactor them,
and write them faster using the interactive console. Codeception tests can be easily organized in groups or Cest classes.

557
vendor/codeception/base/docs/07-BDD.md vendored Normal file
View File

@@ -0,0 +1,557 @@
# Behavior Driven Development
Behavior Driven Development (BDD) is a popular software development methodology. BDD is considered an extension of TDD, and is greatly inspired by [Agile](http://agilemanifesto.org/) practices. The primary reason to choose BDD as your development process is to break down communication barriers between business and technical teams. BDD encourages the use of automated testing to verify all documented features of a project from the very beginning. This is why it is common to talk about BDD in the context of test frameworks (like Codeception). The BDD approach, however, is about much more than testing - it is a common language for all team members to use during the development process.
## What is Behavior Driven Development
BDD was introduced by [Dan North](https://dannorth.net/introducing-bdd/). He described it as:
> outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.
BDD has its own evolution from the days it was born, started by replacing "test" to "should" in unit tests, and moving towards powerful tools like Cucumber and Behat, which made user stories (human readable text) to be executed as an acceptance test.
The idea of story BDD can be narrowed to:
* describe features in a scenario with a formal text
* use examples to make abstract things concrete
* implement each step of a scenario for testing
* write actual code implementing the feature
By writing every feature in User Story format that is automatically executable as a test we ensure that: business, developers, QAs and managers are in the same boat.
BDD encourages exploration and debate in order to formalize the requirements and the features that needs to be implemented by requesting to write the User Stories in a way that everyone can understand.
By making tests to be a part of User Story, BDD allows non-technical personnel to write (or edit) Acceptance tests.
With this procedure we also ensure that everyone in a team knows what has been developed, what has not, what has been tested and what has not.
### Ubiquitous Language
The ubiquitous language is always referred as *common* language. That is it's main benefit. It is not a couple of our business specification's words, and not a couple of developer's technical terms. It is a common words and terms that can be understood by people for whom we are building the software and should be understood by developers. Establishing correct communication between this two groups people is vital for building successful project that will fit the domain and fulfill all business needs.
Each feature of a product should be born from a talk between
* business (analysts, product owner)
* developers
* QAs
which are known in BDD as "three amigos".
Such talks should produce written stories. There should be an actor that doing some things, the feature that should be fulfilled within the story and the result achieved.
We can try to write such simple story:
```
As a customer I want to buy several products
I put first product with $600 price to my cart
And then another one with $1000 price
When I go to checkout process
I should see that total number of products I want to buy is 2
And my order amount is $1600
```
As we can see this simple story highlights core concepts that are called *contracts*. We should fulfill those contracts to model software correctly. But how we can verify that those contracts are being satisfied? [Cucumber](http://cucumber.io) introduced a special language for such stories called **Gherkin**. Same story transformed to Gherkin will look like this:
```gherkin
Feature: checkout process
In order to buy products
As a customer
I want to be able to buy several products
Scenario:
Given I have product with $600 price in my cart
And I have product with $1000 price
When I go to checkout process
Then I should see that total number of products is 2
And my order amount is $1600
```
Cucumber, Behat, and sure, **Codeception** can execute this scenario step by step as an automated test.
Every step in this scenario requires a code which defines it.
## Gherkin
Let's learn some more about Gherkin format and then we will see how to execute it with Codeception:
### Features
Whenever you start writing a story you are describing a specific feature of an application, with a set of scenarios and examples describing this feature.
Feature file is written in Gherkin format. Codeception can generate a feature file for you.
We will assume that we will use scenarios in feature files for acceptance tests, so feature files to be placed in `acceptance` suite directory:
```bash
php vendor/bin/codecept g:feature acceptance checkout
```
Generated template will look like this:
```gherkin
Feature: checkout
In order to ...
As a ...
I need to ...
Scenario: try checkout
```
This template can be fulfilled by setting actor and goals:
```gherkin
Feature: checkout
In order to buy product
As a customer
I need to be able to checkout the selected products
```
Next, we will describe this feature by writing examples for it
#### Scenarios
Scenarios are live examples of feature usage. Inside a feature file it should be written inside a *Feature* block. Each scenario should contain its title:
```gherkin
Feature: checkout
In order to buy product
As a customer
I need to be able to checkout the selected products
Scenario: order several products
```
Scenarios are written in step-by-step manner using Given-When-Then approach. At start, scenario should describe its context with **Given** keyword:
```gherkin
Given I have product with $600 price in my cart
And I have product with $1000 price in my cart
```
Here we also use word **And** to extend the Given and not to repeat it in each line.
This is how we described the initial conditions. Next, we perform some action. We use **When** keyword for it:
```gherkin
When I go to checkout process
```
And in the end we are verifying our expectation using **Then** keyword. The action changed the initial given state, and produced some results. Let's check that those results are what we actually expect.
```gherkin
Then I should see that total number of products is 2
And my order amount is $1600
```
We can test this scenario by executing it in dry-run mode. In this mode test won't be executed (actually, we didn't define any step for it, so it won't be executed in any case).
```bash
$ codecept dry-run acceptance checkout.feature
```
```bash
checkout: order several products
Signature: checkout:order several products
Test: tests/acceptance/checkout.feature:order several products
Scenario --
In order to buy product
As a customer
I need to be able to checkout the selected products
Given i have product with $600 price in my cart
And i have product with $1000 price in my cart
When i go to checkout process
Then i should see that total number of products is 2
And my order amount is $1600
INCOMPLETE
Step definition for `I have product with $600 price in my cart` not found in contexts
Step definition for `I have product with $1000 price` not found in contexts
Step definition for `I go to checkout process` not found in contexts
Step definition for `I should see that total number of products is 2` not found in contexts
Step definition for `my order amount is $1600` not found in contexts
Run gherkin:snippets to define missing steps
```
Besides the scenario steps listed we got the notification that our steps are not defined yet.
We can define them easily by executing `gherkin:snippets` command for the given suite:
```bash
codecept gherkin:snippets acceptance
```
This will produce code templates for all undefined steps in all feature files of this suite.
Our next step will be to define those steps and transforming feature-file into valid test.
### Step Definitions
To match steps from a feature file to PHP code we use annotation which are added to class methods.
By default Codeception expects that all methods marked with `@Given`, `@When`, `@Then` annotation.
Each annotation should contain a step string.
```
/** @Given I am logged as admin */
```
Steps can also be matched with regex expressions. This way we can make more flexible steps
```
/** @Given /I am (logged|authorized) as admin/ */
```
Please note that regular expressions should start and end with `/` char. Regex is also used to match parameters and pass them as arguments into methods.
```php
<?php
/** @Given /I am (?:logged|authorized) as "(\w+)"/ */
function amAuthorized($role)
{
// logged or authorized does not matter to us
// so we added ?: for this capture group
}
```
Parameters can be also passed in non-regex strings using ":" params placeholder.
```
/** @Given I am logged in as :role */
```
This will match any word (passed in double quotes) or a number passed:
```
Given I am logged in as "admin"
Given I am logged in as 1
```
Steps are defined in Context files. Default context is an actor class, i.e. for acceptance testing suite default context is `AcceptanceTester` class. However, you can define steps in any classes and include them as contexts. It is useful to define steps in StepObject and PageObject classes.
To list all defined steps run `gherkin:steps` command:
```bash
codecept gherkin:steps
```
## Testing Behavior
As it was mentioned, feature files is not just a user story.
By writing features in formal language called Gherkin we can execute those scenarios as automated tests.
There is no restrictions in the way how those scenarios are supposed to be tested. Tests can be executed at functional, acceptance, or domain level. However, we will concentrate on acceptance or UI tests in current guide.
### Acceptance Testing
As we generated snippets for missing steps with `gherkin:snippets` command, we will define them in `AcceptanceTester` file.
```php
<?php
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* @Given I have product with :num1 price in my cart
*/
public function iHaveProductWithPriceInMyCart($num1)
{
throw new \Codeception\Exception\Incomplete("Step `I have product with :num1 price in my cart` is not defined");
}
/**
* @When I go to checkout process
*/
public function iGoToCheckoutProcess()
{
throw new \Codeception\Exception\Incomplete("Step `I go to checkout process` is not defined");
}
/**
* @Then I should see that total number of products is :num1
*/
public function iShouldSeeThatTotalNumberOfProductsIs($num1)
{
throw new \Codeception\Exception\Incomplete("Step `I should see that total number of products is :num1` is not defined");
}
/**
* @Then my order amount is :num1
*/
public function myOrderAmountIs($num1)
{
throw new \Codeception\Exception\Incomplete("Step `my order amount is :num1` is not defined");
}
}
```
Please note that `:num1` placeholder can be used for strings and numbers (may contain currency sign).
In current case `:num1` matches `$600` and `$num1` is assigned to be 600. If you need to receive exact string, wrap the value into quotes: `"600$"`
By default they throw Incomplete exceptions to ensure test with missing steps won't be accidentally marked as successful. We will need to implement those steps. As we are in acceptance suite we are probably using [PHPBrowser](http://codeception.com/docs/modules/PhpBrowser) or [WebDriver](http://codeception.com/docs/modules/WebDriver) modules. This means that we can use their methods inside Tester file, as we do with writing tests using `$I->`. You can use `amOnPage`, `click`, `see` methods inside a step definitions, so each Gherkin scenario step to be extended with basic Codeception steps. Let's show how it can be implemented in our case:
```php
<?php
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* @Given I have product with :num1 price in my cart
*/
public function iHaveProductWithPriceInMyCart($num1)
{
// haveRecord method is available in Laravel, Phalcon, Yii modules
$productId = $this->haveRecord('Product', ['name' => 'randomProduct'.uniqid(), 'price' => $num1]);
$this->amOnPage("/item/$productId");
$this->click('Order');
}
/**
* @When I go to checkout process
*/
public function iGoToCheckoutProcess()
{
$this->amOnPage('/checkout');
}
/**
* @Then I should see that total number of products is :num1
*/
public function iShouldSeeThatTotalNumberOfProductsIs($num1)
{
$this->see($num1, '.products-count');
}
/**
* @Then my order amount is :num1
*/
public function myOrderAmountIs($num1)
{
$this->see($num1, '.total');
}
}
```
To make testing more effective we assumed that we are using one of the ActiveRecord frameworks like Laravel, Yii, or Phalcon so we are able to dynamically create records in database with `haveRecord` method. After that we are opening browser and testing our web pages to see that after selecting those products we really see the price was calculated correctly.
We can dry-run (or run) our feature file to see that Given/When/Then are expanded with substeps:
```bash
Given i have product with $600 price in my cart
I have record 'Product',{"name":"randomProduct571fad4f88a04","price":"600"}
I am on page "/item/1"
I click "Order"
And i have product with $1000 price in my cart
I have record 'Product',{"name":"randomProduct571fad4f88b14","price":"1000"}
I am on page "/item/2"
I click "Order"
When i go to checkout process
I am on page "/checkout"
Then i should see that total number of products is 2
I see "2",".products-count"
And my order amount is $1600
I see "1600",".total"
```
This way feature file runs just the same as any other Codeception test. Substeps give us detailed information of how the scenario is being executed.
One of the criticism for testing with Gherkin was that only technical team were aware of how the test scenario is executed. This could have lead to false-positive tests. Developers could have used empty steps for scenarios (or irrelevant ones) and produced invalid tests for valid scenarios. Codeception brings communication to a next level, everyone in a team can understand what happens on a lower (technical) level. Scenario expanding to substeps shows the actual test execution process. Anyone in a team can read the output, and invest their efforts into improving the test suite.
## Advanced Gherkin
Let's improve our BDD suite by using the advanced features of Gherkin language.
### Background
If a group of scenarios have the same initial steps, let's that for dashboard we need always need to be logged in as administrator. We can use *Background* section to do the required preparations and not to repeat same steps across scenarios.
```gherkin
Feature: Dashboard
In order to view current state of business
As an owner
I need to be able to see reports on dashboard
Background:
Given I am logged in as administrator
And I open dashboard page
```
Steps in background are defined the same way as in scenarios.
### Tables
Scenarios can become more descriptive when you represent repeating data as tables. Instead of writing several steps "I have product with :num1 $ price in my cart" we can have one step with multiple values in it.
```gherkin
Given i have products in my cart
| name | category | price |
| Harry Potter | Books | 5 |
| iPhone 5 | Smartphones | 1200 |
| Nuclear Bomb | Weapons | 100000 |
```
Tables is a recommended ways to pass arrays into test scenarios.
Inside a step definition data is stored in argument passed as `\Behat\Gherkin\Node\TableNode` instance.
```php
<?php
/**
* @Given i have products in my cart
*/
public function iHaveProductsInCart(\Behat\Gherkin\Node\TableNode $products)
{
// iterate over all rows
foreach ($node->getRows() as $index => $row) {
if ($index === 0) { // first row to define fields
$keys = $row;
continue;
}
$this->haveRecord('Product', array_combine($keys, $row));
}
}
```
### Examples
In case scenarios represent the same logic but differ on data, we can use *Scenario Outline* to provide different examples for the same behavior. Scenario outline is just like a basic scenario with some values replaced with placeholders, which are filled from a table. Each set of values is executed as a different test.
```gherkin
Scenario Outline: order discount
Given I have product with price <price>$ in my cart
And discount for orders greater than $20 is 10 %
When I go to checkout
Then I should see overall price is "<total>" $
Examples:
| price | total |
| 10 | 10 |
| 20 | 20 |
| 21 | 18.9 |
| 30 | 27 |
| 50 | 45 |
```
### Long Strings
Text values inside a scenarios can be set inside a `"""` block:
```gherkin
Then i see in file "codeception.yml"
"""
paths:
tests: tests
log: tests/_output
data: tests/_data
helpers: tests/_support
envs: tests/_envs
"""
```
This string is passed as a standard PHP string parameter
```php
<?php
/**
* @Then i see in file :filename
*/
public function seeInFile($fileName, $fileContents)
{
// note: module "Asserts" is enabled in this suite
if (!file_exists($fileName)) {
$this->fail("File $fileName not found");
}
$this->assertEquals(file_get_contents($fileName), $fileContents);
}
```
### Tags
Gherkin scenarios and features can contain tags marked with `@`. Tags are equal to groups in Codeception.
This way if you define a feature with `@important` tag, you can execute it inside `important` group by running:
```bash
codecept run -g important
```
Tag should be placed before *Scenario:* or before *Feature:* keyword. In the last case all scenarios of that feature will be added to corresponding group.
## Configuration
As we mentioned earlier, steps should be defined inside context classes. By default all the steps are defined inside an Actor class, for instance, `AcceptanceTester`. However, you can include more contexts. This can be configured inside global `codeception.yml` or suite configuration file:
```yaml
gherkin:
contexts:
default:
- AcceptanceTester
- AdditionalSteps
```
`AdditionalSteps` file should be accessible by autoloader and can be created by `Codeception\Lib\Di`. This means that practically any class can be a context. If a class receives an actor class in constructor or in `_inject` method, DI can inject it into it.
```php
<?php
class AdditionalSteps
{
protected $I;
function __construct(AcceptanceTester $I)
{
$this->I = $I;
}
/**
* @When I do something
*/
function additionalActions()
{
}
}
```
This way PageObjects, Helpers and StepObjects can become contexts as well. But more preferable to include context classes by their tags or roles.
If you have `Step\Admin` class which defines only admin steps, it is a good idea to use it as context for all features containing with "As an admin". In this case "admin" is a role and we can configure it to use additional context.
```yaml
gherkin:
contexts:
role:
admin:
- "Step\Admin"
```
Contexts can be attached to tags as well. This may be useful if you want to redefine steps for some scenarios. Let's say we want to bypass login steps for some scenarios loading already defined session. In this case we can create `Step\FastLogin` class with redefined step "I am logged in as".
```yaml
gherkin:
contexts:
tag:
fastlogin:
- "Step\FastLogin"
```
## Migrating From Behat
While Behat is a great tool for Behavior Driven Development, you still may prefer to use Codeception as your primary testing framework. In case you want to unify all your tests (unit/functional/acceptance), and make them be executed with one runner, Codeception is a good choice. Also Codeception provides rich set of well-maintained modules for various testing backends like Selenium Webdriver, Symfony, Laravel, etc.
If you decided to run your features with Codeception, we recommend to start with symlinking your `features` directory into one of the test suites:
```bash
ln -s $PWD/features tests/acceptance
```
Then you will need to implement all step definitions. Run `gherkin:snippets` to generate stubs for them.
By default it is recommended to place step definitions into actor class (Tester) and use its methods for steps implementation.
## Tests vs Features
It is common to think that BDD scenario is equal to test. But it's actually not. Not every test should be described as a feature. Not every test is written to test real business value. For instance, regression tests or negative scenario tests are not bringing any value to business. Business analysts don't care about scenario reproducing bug #13, or what error message is displayed when user tries to enter wrong password on login screen. Writing all the tests inside a feature files creates informational overflow.
In Codeception you can combine tests written in Gherkin format with tests written in Cept/Cest/Test formats. This way you can keep your feature files compact with minimal set of scenarios, and write regular tests to cover all cases.
Corresponding features and tests can be attached to the same group. And what is more interesting, you can make tests to depend on feature scenarios. Let's say we have `login.feature` file with "Log regular user" scenario in it. In this case you can specify that every test which requires login to pass to depend on "Log regular user" scenario:
```
@depends login:Log regular user
```
Inside `@depends` block you should use test signature. Execute your feature with `dry-run` to see signatures for all scenarios in it. By marking tests with `@depends` you ensure that this test won't be executed before the test it depends on.
## Conclusions
If you like the concept of Behavior Driven Development or prefer to keep test scenarios in human readable format, Codeception allows you to write and execute scenarios in Gherkin. Feature files is just another test format inside Codeception, so it can be combined with Cept and Cest files inside the same suite. Steps definitions of your scenarios can use all the power of Codeception modules, PageObjects, and StepObjects.

View File

@@ -0,0 +1,376 @@
# Customization
In this chapter we will explain how you can extend and customize the file structure and test execution routines.
## One Runner for Multiple Applications
If your project consists of several applications (frontend, admin, api) or you are using the Symfony framework
with its bundles, you may be interested in having all tests for all applications (bundles) executed in one runner.
In this case you will get one report that covers the whole project.
Place the `codeception.yml` file into the root folder of your project
and specify the paths to the other `codeception.yml` configurations that you want to include:
```yaml
include:
- frontend/src/*Bundle
- admin
- api/rest
paths:
output: _output
settings:
colors: false
```
You should also specify the path to the `log` directory, where the reports and logs will be saved.
<div class="alert alert-notice">
Wildcards (*) can be used to specify multiple directories at once.
</div>
### Namespaces
To avoid naming conflicts between Actor classes and Helper classes, they should be separated into namespaces.
To create test suites with namespaces you can add `--namespace` option to the bootstrap command:
```bash
php vendor/bin/codecept bootstrap --namespace frontend
```
This will bootstrap a new project with the `namespace: frontend` parameter in the `codeception.yml` file.
Helpers will be in the `frontend\Codeception\Module` namespace and Actor classes will be in the `frontend` namespace.
Once each of your applications (bundles) has its own namespace and different Helper or Actor classes,
you can execute all the tests in a single runner. Run the Codeception tests as usual, using the meta-config we created earlier:
```bash
php vendor/bin/codecept run
```
This will launch the test suites for all three applications and merge the reports from all of them.
This is very useful when you run your tests on a Continuous Integration server
and you want to get a single report in JUnit and HTML format. The code coverage report will be merged too.
If you want to run a specific suite from the application you can execute:
```
php vendor/bin/codecept run unit -c frontend
```
Where `unit` is the name of suite and the `-c` option specifies the path to the `codeception.yml` configuration file to use.
In this example we will assume that there is `frontend/codeception.yml` configuration file
and that we will execute the unit tests for only that app.
## Extension
Codeception has limited capabilities to extend its core features.
Extensions are not supposed to override current functionality,
but can be useful if you are an experienced developer and you want to hook into the testing flow.
By default, one `RunFailed` Extension is already enabled in your global `codeception.yml`.
It allows you to rerun failed tests by using the `-g failed` option:
```
php vendor/bin/codecept run -g failed
```
Codeception comes with bundled extensions located in `ext` directory.
For instance, you can enable the Logger extension to log the test execution with Monolog:
```yaml
extensions:
enabled:
- Codeception\Extension\RunFailed # default extension
- Codeception\Extension\Logger: # enabled extension
max_files: 5 # logger configuration
```
But what are extensions, anyway? Basically speaking, extensions are nothing more than event listeners
based on the [Symfony Event Dispatcher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html) component.
### Events
Here are the events and event classes. The events are listed in the order in which they happen during execution.
All listed events are available as constants in `Codeception\Events` class.
| Event | When? | Triggered by
|:--------------------:| --------------------------------------- | --------------------------:
| `suite.before` | Before suite is executed | [Suite, Settings](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/SuiteEvent.php)
| `test.start` | Before test is executed | [Test](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/TestEvent.php)
| `test.before` | At the very beginning of test execution | [Codeception Test](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/TestEvent.php)
| `step.before` | Before step | [Step](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/StepEvent.php)
| `step.after` | After step | [Step](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/StepEvent.php)
| `step.fail` | After failed step | [Step](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/StepEvent.php)
| `test.fail` | After failed test | [Test, Fail](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/FailEvent.php)
| `test.error` | After test ended with error | [Test, Fail](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/FailEvent.php)
| `test.incomplete` | After executing incomplete test | [Test, Fail](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/FailEvent.php)
| `test.skipped` | After executing skipped test | [Test, Fail](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/FailEvent.php)
| `test.success` | After executing successful test | [Test](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/TestEvent.php)
| `test.after` | At the end of test execution | [Codeception Test](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/TestEvent.php)
| `test.end` | After test execution | [Test](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/TestEvent.php)
| `suite.after` | After suite was executed | [Suite, Result, Settings](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/SuiteEvent.php)
| `test.fail.print` | When test fails are printed | [Test, Fail](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/FailEvent.php)
| `result.print.after` | After result was printed | [Result, Printer](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Event/PrintResultEvent.php)
There may be some confusion between `test.start`/`test.before` and `test.after`/`test.end`.
The start and end events are triggered by PHPUnit, but the before and after events are triggered by Codeception.
Thus, when you are using classical PHPUnit tests (extended from `PHPUnit\Framework\TestCase`),
the before/after events won't be triggered for them. During the `test.before` event you can mark a test
as skipped or incomplete, which is not possible in `test.start`. You can learn more from
[Codeception internal event listeners](https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Subscriber).
The extension class itself is inherited from `Codeception\Extension`:
```php
<?php
use \Codeception\Events;
class MyCustomExtension extends \Codeception\Extension
{
// list events to listen to
// Codeception\Events constants used to set the event
public static $events = array(
Events::SUITE_AFTER => 'afterSuite',
Events::SUITE_BEFORE => 'beforeTest',
Events::STEP_BEFORE => 'beforeStep',
Events::TEST_FAIL => 'testFailed',
Events::RESULT_PRINT_AFTER => 'print',
);
// methods that handle events
public function afterSuite(\Codeception\Event\SuiteEvent $e) {}
public function beforeTest(\Codeception\Event\TestEvent $e) {}
public function beforeStep(\Codeception\Event\StepEvent $e) {}
public function testFailed(\Codeception\Event\FailEvent $e) {}
public function print(\Codeception\Event\PrintResultEvent $e) {}
}
```
By implementing event handling methods you can listen for events and even update passed objects.
Extensions have some basic methods you can use:
* `write` - prints to the screen
* `writeln` - prints to the screen with a new-line character at the end
* `getModule` - allows you to access a module
* `hasModule` - checks if a module is enabled
* `getModuleNames` - list all enabled modules
* `_reconfigure` - can be implemented instead of overriding the constructor
### Enabling Extension
Once you've implemented a simple extension class, you can require it in `tests/_bootstrap.php`,
load it with Composer's autoloader defined in `composer.json`, or store the class inside `tests/_support`dir.
You can then enable it in `codeception.yml`
```yaml
extensions:
enabled: [MyCustomExtension]
```
Extensions can also be enabled per suite inside suite configs (like `acceptance.suite.yml`) and for a specific environment.
To enable extension dynamically, execute the `run` command with `--ext` option.
Provide a class name as a parameter:
```bash
codecept run --ext MyCustomExtension
codecept run --ext "\My\Extension"
```
If a class is in a `Codeception\Extension` namespace you can skip it and provide only a shortname.
So Recorder extension can be started like this:
```bash
codecept run --ext Recorder
```
### Configuring Extension
In the extension, you can access the currently passed options via the `options` property.
You also can access the global configuration via the `\Codeception\Configuration::config()` method.
If you want to have custom options for your extension, you can pass them in the `codeception.yml` file:
```yaml
extensions:
enabled: [MyCustomExtension]
config:
MyCustomExtension:
param: value
```
The passed in configuration is accessible via the `config` property: `$this->config['param']`.
Check out a very basic extension [Notifier](https://github.com/Codeception/Notifier).
### Custom Commands
You can add your own commands to Codeception.
Your custom commands have to implement the interface Codeception\CustomCommandInterface,
because there has to be a function to get the name of the command.
You have to register your command in the file `codeception.yml`:
```yaml
extensions:
commands: [Project\Command\MyCustomCommand]
```
If you want to activate the Command globally, because you are using more then one `codeception.yml` file,
you have to register your command in the `codeception.dist.yml` in the root folder of your project.
Please see the [complete example](https://github.com/Codeception/Codeception/blob/2.3/tests/data/register_command/examples/MyCustomCommand.php)
## Group Objects
Group Objects are extensions listening for the events of tests belonging to a specific group.
When a test is added to a group:
```php
<?php
/**
* @group admin
*/
public function testAdminCreatingNewBlogPost(\AcceptanceTester $I)
{
}
```
This test will trigger the following events:
* `test.before.admin`
* `step.before.admin`
* `step.after.admin`
* `test.success.admin`
* `test.fail.admin`
* `test.after.admin`
A group object is built to listen for these events. It is useful when an additional setup is required
for some of your tests. Let's say you want to load fixtures for tests that belong to the `admin` group:
```php
<?php
namespace Group;
class Admin extends \Codeception\GroupObject
{
public static $group = 'admin';
public function _before(\Codeception\Event\TestEvent $e)
{
$this->writeln('inserting additional admin users...');
$db = $this->getModule('Db');
$db->haveInDatabase('users', array('name' => 'bill', 'role' => 'admin'));
$db->haveInDatabase('users', array('name' => 'john', 'role' => 'admin'));
$db->haveInDatabase('users', array('name' => 'mark', 'role' => 'banned'));
}
public function _after(\Codeception\Event\TestEvent $e)
{
$this->writeln('cleaning up admin users...');
// ...
}
}
```
GroupObjects can also be used to update the module configuration before running a test.
For instance, for `nocleanup` group we prevent Doctrine2 module from wrapping test into transaction:
```php
<?php
public static $group = 'nocleanup';
public function _before(\Codeception\Event\TestEvent $e)
{
$this->getModule('Doctrine2')->_reconfigure(['cleanup' => false]);
}
```
A group class can be created with `php vendor/bin/codecept generate:group groupname` command.
Group classes will be stored in the `tests/_support/Group` directory.
A group class can be enabled just like you enable an extension class. In the file `codeception.yml`:
``` yaml
extensions:
enabled: [Group\Admin]
```
Now the Admin group class will listen for all events of tests that belong to the `admin` group.
## Custom Reporters
Alternative reporters can be implemented as extension.
There are [DotReporter](http://codeception.com/extensions#DotReporter) and [SimpleReporter](http://codeception.com/extensions#SimpleReporter) extensions included.
Use them to change output or use them as an example to build your own reporter. They can be easily enabled with `--ext` option
```bash
codecept run --ext DotReporter
```
![](https://cloud.githubusercontent.com/assets/220264/26132800/4d23f336-3aab-11e7-81ba-2896a4c623d2.png)
If you want to use it as default reporter enable it in `codeception.yml`.
But what if you need to change the output format of the XML or JSON results triggered with the `--xml` or `--json` options?
Codeception uses PHPUnit printers and overrides them. If you need to customize one of the standard reporters you can override them too.
If you are thinking on implementing your own reporter you should add a `reporters` section to `codeception.yml`
and override one of the standard printer classes with one of your own:
```yaml
reporters:
xml: Codeception\PHPUnit\Log\JUnit
html: Codeception\PHPUnit\ResultPrinter\HTML
report: Codeception\PHPUnit\ResultPrinter\Report
```
All PHPUnit printers implement the
[PHPUnit_Framework_TestListener](https://phpunit.de/manual/current/en/extending-phpunit.html#extending-phpunit.PHPUnit_Framework_TestListener)
interface. It is recommended to read the code of the original reporter before overriding it.
## Installation Templates
Codeception setup can be customized for the needs of your application.
If you build a distributable application and you have a personalized configuration you can build an
Installation template which will help your users to start testing on their projects.
Codeception has built-in installation templates for
* [Acceptance tests](https://github.com/Codeception/Codeception/blob/2.3/src/Codeception/Template/Acceptance.php)
* [Unit tests](https://github.com/Codeception/Codeception/blob/2.3/src/Codeception/Template/Unit.php)
* [REST API tests](https://github.com/Codeception/Codeception/blob/2.3/src/Codeception/Template/Api.php)
They can be executed with `init` command:
```bash
codecept init Acceptance
```
To init tests in specific folder use `--path` option:
```bash
codecept init Acceptance --path acceptance_tests
```
You will be asked several questions and then config files will be generated and all necessary directories will be created.
Learn from the examples above to build a custom Installation Template. Here are the basic rules you should follow:
* Templates should be inherited from [`Codeception\InitTemplate`](http://codeception.com/docs/reference/InitTemplate) class and implement `setup` method.
* Template class should be placed in `Codeception\Template` namespace so Codeception could locate them by class name
* Use methods like `say`, `saySuccess`, `sayWarning`, `sayError`, `ask`, to interact with a user.
* Use `createDirectoryFor`, `createEmptyDirectory` methods to create directories
* Use `createHelper`, `createActor` methods to create helpers and actors.
* Use [Codeception generators](https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Lib/Generator) to create other support classes.
## Conclusion
Each feature mentioned above may help dramatically when using Codeception to automate the testing of large projects,
although some features may require advanced knowledge of PHP. There is no "best practice" or "use cases"
when we talk about groups, extensions, or other powerful features of Codeception.
If you see you have a problem that can be solved using these extensions, then give them a try.

279
vendor/codeception/base/docs/09-Data.md vendored Normal file
View File

@@ -0,0 +1,279 @@
# Working with Data
Tests should not affect each other. That's a rule of thumb. When tests interact with a database,
they may change the data inside it, which would eventually lead to data inconsistency.
A test may try to insert a record that has already been inserted, or retrieve a deleted record.
To avoid test failures, the database should be brought back to its initial state before each test.
Codeception has different methods and approaches to get your data cleaned.
This chapter summarizes all of the notices on clean-ups from the previous chapters
and suggests the best strategies of how to choose data storage backends.
When we decide to clean up a database, we should make this cleaning as fast as possible. Tests should always run fast.
Rebuilding the database from scratch is not the best way, but might be the only one. In any case,
you should use a special test database for testing. **Do not ever run tests on development or production databases!**
## Db
Codeception has a `Db` module, which takes on most of the tasks of database interaction.
```yaml
modules:
config:
Db:
dsn: 'PDO DSN HERE'
user: 'root'
password:
```
<div class="alert alert-notice">
Use <a href="http://codeception.com/docs/06-ModulesAndHelpers#Dynamic-Configuration-With-Params">module parameters</a>
to set the database credentials from environment variables or from application configuration files.
</div>
Db module can cleanup database between tests by loading a database dump. This can be done by parsing SQL file and
executing its commands using current connection
```yaml
modules:
config:
Db:
dsn: 'PDO DSN HERE'
user: 'root'
password:
dump: tests/_data/your-dump-name.sql
cleanup: true # reload dump between tests
populate: true # load dump before all tests
```
Alternatively an external tool (like mysql client, or pg_restore) can be used. This approach is faster and won't produce parsing errors while loading a dump.
Use `populator` config option to specify the command. For MySQL it can look like this:
```yaml
modules:
enabled:
- Db:
dsn: 'mysql:host=localhost;dbname=testdb'
user: 'root'
password: ''
cleanup: true # run populator before each test
populate: true # run populator before all test
populator: 'mysql -u $user $dbname < tests/_data/dump.sql'
```
See the [Db module reference](http://codeception.com/docs/modules/Db#SQL-data-dump) for more examples.
To ensure database dump is loaded before all tests add `populate: true`. To clean current database and reload dump between tests use `cleanup: true`.
<div class="alert alert-notice">
A full database clean-up can be painfully slow if you use large database dumps. It is recommended to do more data testing
on the functional and integration levels, this way you can get performance bonuses from using ORM.
</div>
In acceptance tests, your tests are interacting with the application through a web server. This means that the test
and the application work with the same database. You should provide the same credentials in the Db module
that your application uses, then you can access the database for assertions (`seeInDatabase` actions)
and to perform automatic clean-ups.
The Db module provides actions to create and verify data inside a database.
If you want to create a special database record for one test,
you can use [`haveInDatabase`](http://codeception.com/docs/modules/Db#haveInDatabase) method of `Db` module:
```php
<?php
$I->haveInDatabase('posts', [
'title' => 'Top 10 Testing Frameworks',
'body' => '1. Codeception'
]);
$I->amOnPage('/posts');
$I->see('Top 10 Testing Frameworks');
```
`haveInDatabase` inserts a row with the provided values into the database.
All added records will be deleted at the end of the test.
If you want to check that a table record was created
use [`seeInDatabase`](http://codeception.com/docs/modules/Db#haveInDatabase) method:
```php
<?php
$I->amOnPage('/posts/1');
$I->fillField('comment', 'This is nice!');
$I->click('Submit');
$I->seeInDatabase('comments', ['body' => 'This is nice!']);
```
See the module [reference](http://codeception.com/docs/modules/Db) for other methods you can use for database testing.
There are also modules for [MongoDb](http://codeception.com/docs/modules/MongoDb),
[Redis](http://codeception.com/docs/modules/Redis),
and [Memcache](http://codeception.com/docs/modules/Memcache) which behave in a similar manner.
### Sequence
If the database clean-up takes too long, you can follow a different strategy: create new data for each test.
This way, the only problem you might face is duplication of data records.
[Sequence](http://codeception.com/docs/modules/Sequence) was created to solve this.
It provides the `sq()` function which generates unique suffixes for creating data in tests.
## ORM modules
Your application is most likely using object-relational mapping (ORM) to work with the database. In this case,
Codeception allows you to use the ORM methods to work with the database, instead of accessing the database directly.
This way you can work with models and entities of a domain, and not on tables and rows.
By using ORM in functional and integration tests, you can also improve performance of your tests.
Instead of cleaning up the database after each test, the ORM module will wrap all the database actions into transactions
and roll it back at the end. This way, no actual data will be written to the database.
This clean-up strategy is enabled by default,
you can disable it by setting `cleanup: false` in the configuration of any ORM module.
### ActiveRecord
Popular frameworks like Laravel, Yii, and Phalcon include an ActiveRecord data layer by default.
Because of this tight integration, you just need to enable the framework module, and use its configuration for database access.
Corresponding framework modules provide similar methods for ORM access:
* `haveRecord`
* `seeRecord`
* `dontSeeRecord`
* `grabRecord`
They allow you to create and check data by model name and field names in the model. Here is the example in Laravel:
```php
<?php
// create record and get its id
$id = $I->haveRecord('posts', ['body' => 'My first blogpost', 'user_id' => 1]);
$I->amOnPage('/posts/'.$id);
$I->see('My first blogpost', 'article');
// check record exists
$I->seeRecord('posts', ['id' => $id]);
$I->click('Delete');
// record was deleted
$I->dontSeeRecord('posts', ['id' => $id]);
```
<div class="alert alert-notice">
Laravel5 module also provides `haveModel`, `makeModel` methods which use factories to generate models with fake data.
</div>
If you want to use ORM for integration testing only, you should enable the framework module with only the `ORM` part enabled:
```yaml
modules:
enabled:
- Laravel5:
- part: ORM
```
```yaml
modules:
enabled:
- Yii2:
- part: ORM
```
This way no web actions will be added to `$I` object.
If you want to use ORM to work with data inside acceptance tests, you should also include only the ORM part of a module.
Please note that inside acceptance tests, web applications work inside a webserver, so any test data can't be cleaned up
by rolling back transactions. You will need to disable cleaning up,
and use the `Db` module to clean the database up between tests. Here is a sample config:
```yaml
modules:
enabled:
- WebDriver:
url: http://localhost
browser: firefox
- Laravel5:
cleanup: false
- Db
```
### DataMapper
Doctrine is also a popular ORM, unlike some others it implements the DataMapper pattern and is not bound to any framework.
The [Doctrine2](http://codeception.com/docs/modules/Doctrine2) module requires an `EntityManager` instance to work with.
It can be obtained from a Symfony framework or Zend Framework (configured with Doctrine):
```yaml
modules:
enabled:
- Symfony
- Doctrine2:
depends: Symfony
```
```yaml
modules:
enabled:
- ZF2
- Doctrine2:
depends: ZF2
```
If no framework is used with Doctrine you should provide the `connection_callback` option
with a valid callback to a function which returns an `EntityManager` instance.
Doctrine2 also provides methods to create and check data:
* `haveInRepository`
* `grabFromRepository`
* `grabEntitiesFromRepository`
* `seeInRepository`
* `dontSeeInRepository`
### DataFactory
Preparing data for testing is a very creative, although boring, task. If you create a record,
you need to fill in all the fields of the model. It is much easier to use [Faker](https://github.com/fzaninotto/Faker)
for this task, which is more effective to set up data generation rules for models.
Such a set of rules is called *factories*
and are provided by the [DataFactory](http://codeception.com/docs/modules/DataFactory) module.
Once configured, it can create records with ease:
```php
<?php
// creates a new user
$user_id = $I->have('App\Model\User');
// creates 3 posts
$I->haveMultiple('App\Model\Post', 3);
```
Created records will be deleted at the end of a test.
The DataFactory module only works with ORM, so it requires one of the ORM modules to be enabled:
```yaml
modules:
enabled:
- Yii2:
configFile: path/to/config.php
- DataFactory:
depends: Yii2
```
```yaml
modules:
enabled:
- Symfony
- Doctrine2:
depends: Symfony
- DataFactory:
depends: Doctrine2
```
DataFactory provides a powerful solution for managing data in integration/functional/acceptance tests.
Read the [full reference](http://codeception.com/docs/modules/DataFactory) to learn how to set this module up.
## Conclusion
Codeception also assists the developer when dealing with data. Tools for database population
and cleaning up are bundled within the `Db` module. If you use ORM, you can use one of the provided framework modules
to operate with database through a data abstraction layer, and use the DataFactory module to generate new records with ease.

View File

@@ -0,0 +1,287 @@
# Testing WebServices
The same way we tested a web site, Codeception allows you to test web services. They are very hard to test manually, so it's a really good idea to automate web service testing. We have SOAP and REST as standards, which are represented in corresponding modules, which we will cover in this chapter.
You should start by creating a new test suite, (which was not provided by the `bootstrap` command). We recommend calling it **api** and using the `ApiTester` class for it.
```bash
$ php vendor/bin/codecept generate:suite api
```
We will put all the api tests there.
## REST
The REST web service is accessed via HTTP with standard methods: `GET`, `POST`, `PUT`, `DELETE`. They allow users to receive and manipulate entities from the service. Accessing a WebService requires an HTTP client, so for using it you need the module `PhpBrowser` or one of framework modules set up. For example, we can use the `Symfony` module for Symfony2 applications in order to ignore web server and test web service internally.
Configure modules in `api.suite.yml`:
``` yaml
actor: ApiTester
modules:
enabled:
- REST:
url: http://serviceapp/api/v1/
depends: PhpBrowser
```
The REST module will connect to `PhpBrowser` according to this configuration. Depending on the web service we may deal with XML or JSON responses. Codeception handles both data formats well, however If you don't need one of them, you can explicitly specify that the JSON or XML parts of the module will be used:
``` yaml
actor: ApiTester
modules:
enabled:
- REST:
url: http://serviceapp/api/v1/
depends: PhpBrowser
part: Json
```
API tests can be functional and be executed using Symfony, Laravel5, Zend, or any other framework module. You will need slightly update configuration for it:
``` yaml
actor: ApiTester
modules:
enabled:
- REST:
url: /api/v1/
depends: Laravel5
```
Once we have configured our new testing suite, we can create the first sample test:
```bash
$ codecept generate:cest api CreateUser
```
It will be called `CreateUserCest.php`.
We need to implement a public method for each test. Let's make `createUserViaAPI` to test creation of a user via the REST API.
```php
<?php
class CreateUserCest
{
public function _before(\ApiTester $I)
{
}
public function _after(\ApiTester $I)
{
}
// tests
public function createUserViaAPI(\ApiTester $I)
{
$I->amHttpAuthenticated('service_user', '123456');
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
$I->sendPOST('/users', ['name' => 'davert', 'email' => 'davert@codeception.com']);
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseContains('{"result":"ok"}');
}
}
```
We can use HTTP code constants from `Codeception\Util\HttpCode` instead of numeric values to check response code in `seeResponseCodeIs` and `dontSeeResponseCodeIs` methods.
### Testing JSON Responses
The last line of the previous example verified that the response contained the provided string. However we shouldn't rely on it, as depending on content formatting we can receive different results with the same data. What we actually need is to check that the response can be parsed and it contains some of the values we expect. In the case of JSON we can use the `seeResponseContainsJson` method
``` php
<?php
// matches {"result":"ok"}'
$I->seeResponseContainsJson(['result' => 'ok']);
// it can match tree-like structures as well
$I->seeResponseContainsJson([
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
]);
```
You may want to perform even more complex assertions on a response. This can be done by writing your own methods in the [Helper](http://codeception.com/docs/06-ReusingTestCode#Modules-and-Helpers) classes. To access the latest JSON response you will need to get the `response` property of the `REST` module. Let's demonstrate it with the `seeResponseIsHtml` method:
```php
<?php
namespace Helper;
class Api extends \Codeception\Module
{
public function seeResponseIsHtml()
{
$response = $this->getModule('REST')->response;
$this->assertRegExp('~^<!DOCTYPE HTML(.*?)<html>.*?<\/html>~m', $response);
}
}
```
The same way you can receive request parameters and headers.
### Validate JSON structures
It is pretty common for API tests to not only validate the received data but to check the structure of the response. Response data is not usually considered to be consistent, and may change on each request, however the JSON/XML structure should be kept the same for an API version. In order to check response structure the REST module has some useful methods.
If we expect a JSON response to be received we can check its structure with [JSONPath](http://goessner.net/articles/JsonPath/). It looks and sounds like XPath but is designed to work with JSON data, however we can convert JSON into XML and use XPath to validate the structure. Both approaches are valid and can be used in the REST module:
```php
<?php
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseJsonMatchesJsonPath('$[0].user.login');
$I->seeResponseJsonMatchesXpath('//user/login');
```
More detailed check can be applied if you need to validate the type of fields in a response.
You can do that by using with a [seeResponseMatchesJsonType](http://codeception.com/docs/modules/REST#seeResponseMatchesJsonType) action in which you define the structure of JSON response.
```php
<?php
$I->sendGET('/users/1');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseMatchesJsonType([
'id' => 'integer',
'name' => 'string',
'email' => 'string:email',
'homepage' => 'string:url|null',
'created_at' => 'string:date',
'is_active' => 'boolean'
]);
```
Codeception uses this simple and lightweight definitions format which can be [easily learned and extended](http://codeception.com/docs/modules/REST#seeResponseMatchesJsonType).
### Testing XML Responses
In case your REST API works with XML format you can use similar methods to test its data and structure.
There is `seeXmlResponseIncludes` method to match inclusion of XML parts in response, and `seeXmlResponseMatchesXpath` to validate its structure.
```php
<?php
$I->sendGET('/users.xml');
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
$I->seeResponseIsXml();
$I->seeXmlResponseMatchesXpath('//user/login');
$I->seeXmlResponseIncludes(\Codeception\Util\Xml::toXml([
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
]));
```
We are using `Codeception\Util\Xml` class which allows us to build XML structures in a clean manner. The `toXml` method may accept a string or array and returns \DOMDocument instance. If your XML contains attributes and so can't be represented as a PHP array you can create XML using the [XmlBuilder](http://codeception.com/docs/reference/XmlBuilder) class. We will take a look at it a bit more in next section.
<div class="alert alert-info">
Use `\Codeception\Util\Xml::build()` to create XmlBuilder instance.
</div>
## SOAP
SOAP web services are usually more complex. You will need PHP [configured with SOAP support](http://php.net/manual/en/soap.installation.php). Good knowledge of XML is required too. `SOAP` module uses specially formatted POST request to connect to WSDL web services. Codeception uses `PhpBrowser` or one of framework modules to perform interactions. If you choose using a framework module, SOAP will automatically connect to the underlying framework. That may improve the speed of a test execution and will provide you with more detailed stack traces.
Let's configure `SOAP` module to be used with `PhpBrowser`:
``` yaml
actor: ApiTester
modules:
enabled:
- SOAP:
depends: PhpBrowser
endpoint: http://serviceapp/api/v1/
```
SOAP request may contain application specific information, like authentication or payment. This information is provided with SOAP header inside the `<soap:Header>` element of XML request. In case you need to submit such header, you can use `haveSoapHeader` action. For example, next line of code
```php
<?php
$I->haveSoapHeader('Auth', array('username' => 'Miles', 'password' => '123456'));
```
will produce this XML header
```xml
<soap:Header>
<Auth>
<username>Miles</username>
<password>123456</password>
</Auth>
</soap:Header>
```
Use `sendSoapRequest` method to define the body of your request.
```php
<?php
$I->sendSoapRequest('CreateUser', '<name>Miles Davis</name><email>miles@davis.com</email>');
```
This call will be translated to XML:
```xml
<soap:Body>
<ns:CreateUser>
<name>Miles Davis</name>
<email>miles@davis.com</email>
</ns:CreateUser>
</soap:Body>
```
And here is the list of sample assertions that can be used with SOAP.
```php
<?php
$I->seeSoapResponseEquals('<?xml version="1.0"<error>500</error>');
$I->seeSoapResponseIncludes('<result>1</result>');
$I->seeSoapResponseContainsStructure('<user><name></name><email></email>');
$I->seeSoapResponseContainsXPath('//result/user/name[@id=1]');
```
In case you don't want to write long XML strings, consider using [XmlBuilder](http://codeception.com/docs/reference/XmlBuilder) class. It will help you to build complex XMLs in jQuery-like style.
In the next example we will use `XmlBuilder` instead of regular XML.
```php
<?php
$I->haveSoapHeader('Session', array('token' => '123456'));
$I->sendSoapRequest('CreateUser', Xml::build()
->user->email->val('miles@davis.com'));
$I->seeSoapResponseIncludes(\Codeception\Util\Xml::build()
->result->val('Ok')
->user->attr('id', 1)
);
```
It's up to you to decide whether to use `XmlBuilder` or plain XML. `XmlBuilder` will return XML string as well.
You may extend current functionality by using `SOAP` module in your helper class. To access the SOAP response as `\DOMDocument` you can use `response` property of `SOAP` module.
```php
<?php
namespace Helper;
class Api extends \Codeception\Module {
public function seeResponseIsValidOnSchema($schema)
{
$response = $this->getModule('SOAP')->response;
$this->assertTrue($response->schemaValidate($schema));
}
}
```
## Conclusion
Codeception has two modules that will help you to test various web services. They need a new `api` suite to be created. Remember, you are not limited to test only response body. By including `Db` module you may check if a user has been created after the `CreateUser` call. You can improve testing scenarios by using REST or SOAP responses in your helper methods.

View File

@@ -0,0 +1,133 @@
# Code Coverage
At some point you want to review which parts of your application are tested well and which are not.
Just for this case the [CodeCoverage](http://en.wikipedia.org/wiki/Code_coverage) is used. When you execute your tests to collect coverage report,
you will receive statistics of all classes, methods, and lines triggered by these tests.
The ratio between all lines in script and all touched lines is a main coverage criterion. In the ideal world you should get 100% code coverage, but in reality 80% is really enough. Because even 100% code coverage rate doesn't save you from fatal errors and crashes.
*To collect coverage information `xdebug` is required**.
![Code Coverage Example](http://codeception.com/images/coverage.png)
Coverage data can be collected manually for both local and remote tests. Remote tests may be executed on different nodes,
or locally but running through web server. It may look hard to collect code coverage for Selenium tests or PhpBrowser tests. But Codeception supports remote codecoverage as well as local.
### Configuration
To enable code coverage put these lines in the global configuration file `codeception.yml`:
``` yaml
coverage:
enabled: true
```
That's ok for now. But what files should be present in final coverage report?
Pass an array of files or directory to include/exclude sections. The path ending with '\*' matches the directory.
Also you can use '\*' mask in a file name, i.e. `app/models/*Model.php` to match all models.
There is a shortcut if you don't need that complex filters:
``` yaml
coverage:
enabled: true
include:
- app/*
exclude:
- app/cache/*
```
Include and exclude options can be redefined for each suite in corresponding config files.
By default, if coverage is reported to be < 35% it is marked as low, and >70% is high coverage.
You can also define high and low boundaries with `low_limit` and `high_limit` config options:
```yaml
coverage:
enabled: true
low_limit: 30
high_limit: 60
```
## Local CodeCoverage
The basic codecoverage can be collected for functional and unit tests.
If you performed configuration steps from above, you are ready to go.
All you need is to execute codeception with `--coverage` option.
To generate a clover xml report or a tasty html report append also `--coverage-xml` and `--coverage-html` options.
``` yaml
codecept run --coverage --coverage-xml --coverage-html
```
XML and HTML reports are stored to the `_output` directory. The best way to review report is to open `index.html` from `tests/_output/coverage` in your browser.
XML clover reports are used by IDEs (like PHPStorm) or Continuous Integration servers (like Jenkins).
## Remote CodeCoverage
### Local Server
If you run your application via Webserver (Apache, Nginx, PHP WebServer) you don't have a direct access to tested code,
so collecting coverage becomes a non-trivial task. The same goes for scripts that are tested on different nodes.
To get access to this code you need `xdebug` installed with `remote_enable` option turned on.
Codeception also requires a little spy to interact with your application. As your application runs standalone,
without even knowing it is being tested, a small file should be included in order to collect coverage info.
This file is called `c3.php` and is [available on GitHub](https://github.com/Codeception/c3).
`c3.php` should be downloaded and included in your application at the very first line from controller.
By sending special headers Codeception will command your application when to start codecoverage collection and when to stop it.
After the suite is finished, a report will be stored and Codeception will grab it from your application.
Please, follow installation instructions described in a [readme file](https://github.com/Codeception/c3).
To connect to `c3` Codeception uses url config from PhpBrowser or WebDriver module.
But URL of index with `c3.php` included can be specified explicitly with `c3_url` parameter defined:
``` yaml
coverage:
# url of file which includes c3 router.
c3_url: 'http://127.0.0.1:8000/index-test.php/'
```
> note: we can't have multiple `c3_url` on same host difference only by port. Please, use alias of domain
(i.e. `frontend.dev:8000`,`backend.dev:8080`) instead.
After the `c3.php` file is included in application you can start gather coverage.
In case you execute your application locally there is nothing to be changed in config.
All codecoverage reports will be collected as usual and merged afterwards.
Think of it: Codeception runs remote coverage in the same way as local.
### Remote Server
But if you run tests on different server (or your webserver doesn't use code from current directory) a single option `remote` should be added to config.
For example, let's turn on remote coverage for acceptance suite in `acceptance.suite.yml`:
``` yaml
coverage:
remote: true
```
In this case remote Code Coverage results won't be merged with local ones, if this option is enabled.
Merging is possible only in case a remote and local files have the same path.
But in case of running tests on a remote server we are not sure of it.
CodeCoverage results from remote server will be saved to `tests/_output` directory. Please note that remote codecoverage results won't be displayed in console by the reason mentioned above: local and remote results can't be merged, and console displays results for local codecoverage.
### Remote Context Options
HTML report generation can at times take a little more time than the default 30 second timeout. Or maybe you want to alter SSL settings (verify_peer, for example)
To alter the way c3 sends it's service requests to your webserver (be it a local or a remote one), you can use the `remote_context_options` key in `coverage` settings.
``` yaml
coverage:
remote_context_options:
http:
timeout: 60
ssl:
verify_peer: false
```
Context stream options are [well documented at php.net](http://php.net/manual/en/context.php)
## Conclusion
It's never been easier to setup local and remote code coverage. Just one config and one additional file to include!
**With Codeception you can easily generate CodeCoverage reports for your Selenium tests** (or other acceptance or api tests). Mixing reports for `acceptance`, `functional`, and `unit` suites provides you with the most complete information on which parts of your applications are tested and which are not.

View File

@@ -0,0 +1,156 @@
# Continuous Integration
Once you get testing suite up and running you are interested in running your tests regularly. If you ensure that tests are running on every code change or at least once a day you can be sure that no regression is introduced. This allows to keep you system stable. But developers are not so passionate about running all tests manually, they also can forget to execute tests before pushing code to production... The solution is simple, test execution should be automated. Instead of running them locally it is better to have dedicated server responsible for running tests for a team. This way we can ensure that everyone's tests executed, which commit made a regression in codebase, and that we can deploy only once tests pass.
There are many Continuous Integration Servers out there. We will try to list basic steps to setup Codeception tests with them. If your CI system is not mentioned, you can get the idea by analogy. Please also help us to extend this guide by adding instructions for different CIs.
## Jenkins
![Jenkins](http://codeception.com/images/jenkins/Jenk1.png)
[Jenkins](http://jenkins-ci.org/) is one of the most popular open-source solution on market. It is easy to setup and is easy to customize by applying various plugins.
![Create new job in Jenkins](http://codeception.com/images/jenkins/Jenk2.png)
### Preparing Jenkins
It is recommended to have the next plugins installed:
* **Git Plugin** - for building tests for Git repo
* **Green Balls** - to display success results in green.
* **xUnit Plugin**, **jUnit Plugin** - to process and display Codeception XML reports
* **HTML Publisher Plugin** - to process Codeception HTML reports
* **AnsiColor** - to show colorized console output.
![Jenkins Plugins](http://codeception.com/images/jenkins/Jenk3.png)
### Basic Setup
At first we need to create build project. Depending on your needs you can set up periodical build or trigger build once the change is pushed to GitHub (you will need GitHub plugin for that).
We need to define build steps. The most simple setup may look like this:
```
php vendor/bin/codecept run
```
![Jenkins Codeception Build Step](http://codeception.com/images/jenkins/Jenk5.png)
Then we can start the very first job and check the execution progress. If tests fail we will see that in console:
![Jenkins Console Output](http://codeception.com/images/jenkins/Jenk6.png)
### XML Reports
But we don't want to analyze console output for each failing build. Especially If Jenkins can collect and display the results inside its web UI. Codeception can export its results using JUnit XML format. To generate XML report on each build we will need to append `--xml` option to Codeception execution command. Codeception will print `result.xml` file containing information about test status with steps and stack traces for failing tests.
Now let's update our build step to generate xml:
```
php vendor/bin/codecept run --xml
```
and ask Jenkins to collect resulted XML. This can be done as part of Post-build actions. Let's add *Publish xUnit test result report* action and configure it to use with PHPUnit reports.
![Use PHPUnit xUnit builder for Jenkins](http://codeception.com/images/jenkins/Jenk7.png)
Now we should specify path to PHPUnit style XML reports. In case of standard Codeception setup we should specify `tests/_output/*.xml` as a pattern for matching resulted XMLs. Now we save the project and rebuild it.
![Jenkins Result Trend](http://codeception.com/images/jenkins/Jenk8.png)
Now for all builds we will see results trend graph that shows us percentage of passing and failing tests. We also will see a **Latest Test Result** link which will lead to to the page where all executed tests and their stats listed in a table.
### HTML Reports
To get more details on steps executed you can generate HTML report and use Jenkins to display them.
```
php vendor/bin/codecept run --html
```
Now we need HTML Publisher plugin configured to display generated HTML files. It should be added as post-build action similar way we did it for XML reports.
![Jenkins Codeception HTML Setup](http://codeception.com/images/jenkins/Jenk9.png)
Jenkins should locate `report.html` at `tests/_output/`. Now Jenkins will display HTML reports for each build.
![Jenkins HTML Report](http://codeception.com/images/jenkins/Jenki10.png)
![Jenkins Codeception HTML Results](http://codeception.com/images/jenkins/Jenki11.png)
## TeamCity
![TeamCity](http://codeception.com/images/teamcity/logo.jpg)
TeamCity is a hosted solution from JetBrains. The setup of it can be a bit tricky as TeamCity uses its own reporter format for parsing test results. PHPUnit since version 5.x has integrated support for this format, so does Codeception. What we need to do is to configure Codeception to use custom reporter. By default there is `--report` option which provides an alternative output. You can change the reporter class in `codeception.yml` configuration:
```yaml
reporters:
report: PHPUnit_Util_Log_TeamCity
```
As an alternative you can use 3rd-party [TeamCity extension](https://github.com/neronmoon/TeamcityCodeception) for better reporting.
After you create build project you should define build step with Codeception which is
```
php vendor/bin/codecept run --report
```
![build step](http://codeception.com/images/teamcity/build.png)
Once you execute your first build you should see detailed report inside TeamCity interface:
![report](http://codeception.com/images/teamcity/report2.png)
## TravisCI
![Travis CI](http://codeception.com/images/travis.png)
Travis CI is popular service CI with good GitHub integration. Codeception is self-tested with Travis CI. There nothing special about configuration. Just add to the bottom line of travis configuration:
```yaml
php vendor/bin/codecept run
```
More details on configuration can be learned from Codeception's [`.travis.yml`](https://github.com/Codeception/Codeception/blob/master/.travis.yml).
Travis doesn't provide visualization for XML or HTML reports so you can't view reports in format any different than console output. However, Codeception produces nice console output with detailed error reports.
## GitLab
![report](http://codeception.com/images/gitlab/logo.png)
If a file `.gitlab-ci.yml` exists in the root of the git repository, GitLab will run a pipeline each time you push to the gitlab server. The file configures the docker image that will be called. Below is a sample which loads a php7 docker image, clones your files, installs composer dependencies, runs the built-in php webserver and finally runs codeception:
```yaml
# Select image from https://hub.docker.com/_/php/
image: php:7.0
# Select what we should cache
cache:
paths:
- vendor/
before_script:
# Install git and unzip (composer will need them)
- apt-get update && apt-get install -qqy git unzip
# Install composer
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install all project dependencies
- composer install
# Run webserver
- php -S localhost:8085 --docroot public &>/dev/null&
# Test
test:
script:
- vendor/bin/codecept run
```
![report](http://codeception.com/images/gitlab/build.png)
For acceptance testing you can use `codeception/codeception` docker image as base.
## Conclusion
It is highly recommended to use Continuous Integration system in development. Codeception is easy to install and run in any CI systems. However, each of them has their differences you should take into account. You can use different reporters to provide output in format expected by CI system.

View File

@@ -0,0 +1,420 @@
# Parallel Execution
When execution time of your tests is longer than a coffee break, it is a good reason to think about making your tests faster. If you have already tried to run them on SSD drive, or to use PhantomJS instead of Selenium, and the execution time still upsets you, it might be a good idea to run your tests in parallel.
## Where to start
Codeception does not provide a command like `run-parallel`. There is no common solution that can play well for everyone. Here are the questions you will need to answer:
* How parallel processes will be executed?
* How parallel processes won't affect each other?
* Will they use different databases?
* Will they use different hosts?
* How should I split my tests across parallel processes?
There are two approaches to achieve parallelization. We can use [Docker](http://docker.com) and run each process inside isolated containers, and have those containers executed simultaneously.
Docker works really well for isolating testing environments.
By the time of writing this chapter, we didn't have an awesome tool like it. This chapter demonstrates how to manage parallel execution manually. As you will see we spend too much effort trying to isolate tests which Docker does for free. Today we <strong>recommend using Docker</strong> for parallel testing.
## Docker
Please make sure you have `docker` or [Docker Toolbox](https://www.docker.com/products/docker-toolbox) installed. Docker experience is required as well.
### Using Codeception Docker image
Run official Codeception image from DockerHub:
docker run codeception/codeception
Running tests from a project, by mounting the current path as a host-volume into the container.
The **default working directory in the container is `/project`**.
docker run -v ${PWD}:/project codeception/codeception run
To prepare application and tests to be executed inside containers you will need to use [Docker Compose](https://docs.docker.com/compose/) to run multiple containers and connect them together.
Define all required services in `docker-compose.yml` file. Make sure to follow Docker philisophy: 1 service = 1 container. So each process should be defined as its own service. Those services can use official Docker images pulled from DockerHub. Directories with code and tests should be mounted using `volume` directive. And exposed ports should be explicitly set using `ports` directive.
We prepared a sample config with codeception, web server, database, and selenium with firefox to be executed together.
```yaml
version: '3'
services:
codecept:
image: codeception/codeception
depends_on:
- chrome
- web
volumes:
- .:/project
web:
image: php:7-apache
depends_on:
- db
volumes:
- .:/var/www/html
db:
image: percona:5.6
chrome:
image: selenium/standalone-chrome
```
Codeception service will execute command `codecept run` but only after all services are started. This is defined using `depends_on` parameter.
It is easy to add more custom services. For instance to use Redis you just simple add this lines:
```yaml
redis:
image: redis:3
```
By default the image has codecept as its entrypoint, to run the tests simply supply the run command
```
docker-compose run --rm codecept help
```
Run suite
```
docker-compose run --rm codecept run acceptance
```
```
docker-compose run --rm codecept run acceptance LoginCest
```
Development bash
```
docker-compose run --rm --entrypoint bash codecept
```
And finally to execute testing in parallel you should define how you split your tests and run parallel processes for `docker-compose`. Here we split tests by suites, but you can use different groups to split your tests. In section below you will learn how to do that with Robo.
```
docker-compose --project-name test-web run -d --rm codecept run --html report-web.html web & \
docker-compose --project-name test-unit run -d --rm codecept run --html report-unit.html unit & \
docker-compose --project-name test-functional run -d --rm codecept run --html report-functional.html functional
```
At the end, it is worth specifying that Docker setup can be complicated and please make sure you understand Docker and Docker Compose before proceed. We prepared some links that might help you:
* [Acceptance Tests Demo Repository](https://github.com/dmstr/docker-acception)
* [Dockerized Codeception Internal Tests](https://github.com/Codeception/Codeception/blob/master/tests/README.md#dockerized-testing)
* [Phundament App with Codeception](https://gist.github.com/schmunk42/d6893a64963509ff93daea80f722f694)
If you want to automate splitting tests by parallel processes, and executing them using PHP script you should use Robo task runner to do that.
## Robo
### What to do
Parallel Test Execution consists of 3 steps:
* splitting tests
* running tests in parallel
* merging results
We propose to perform those steps using a task runner. In this guide we will use [**Robo**](http://robo.li) task runner. It is a modern PHP task runner that is very easy to use. It uses [Symfony Process](http://symfony.com/doc/current/components/process.html) to spawn background and parallel processes. Just what we need for the step 2! What about steps 1 and 3? We have created robo [tasks](https://github.com/Codeception/robo-paracept) for splitting tests into groups and merging resulting JUnit XML reports.
To conclude, we need:
* [Robo](http://robo.li), a task runner.
* [robo-paracept](https://github.com/Codeception/robo-paracept) - Codeception tasks for parallel execution.
## Preparing Robo and Robo-paracept
Execute this command in an empty folder to install Robo and Robo-paracept :
```bash
$ composer require codeception/robo-paracept:dev-master
```
You need to install Codeception after, if codeception is already installed it will not work.
```bash
$ composer require codeception/codeception
```
### Preparing Robo
Initializes basic RoboFile in the root of your project
```bash
$ robo init
```
Open `RoboFile.php` to edit it
```php
<?php
class RoboFile extends \Robo\Tasks
{
// define public methods as commands
}
```
Each public method in robofile can be executed as a command from console. Let's define commands for 3 steps and include autoload.
```php
<?php
require_once 'vendor/autoload.php';
class Robofile extends \Robo\Tasks
{
use \Codeception\Task\MergeReports;
use \Codeception\Task\SplitTestsByGroups;
public function parallelSplitTests()
{
}
public function parallelRun()
{
}
public function parallelMergeResults()
{
}
}
```
If you run `robo`, you can see the respective commands:
```bash
$ robo
Robo version 0.6.0
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
help Displays help for a command
list Lists commands
parallel
parallel:merge-results
parallel:run
parallel:split-tests
```
#### Step 1: Split Tests
Codeception can organize tests into [groups](http://codeception.com/docs/07-AdvancedUsage#Groups). Starting from 2.0 it can load information about a group from a files. Sample text file with a list of file names can be treated as a dynamically configured group. Take a look into sample group file:
```bash
tests/functional/LoginCept.php
tests/functional/AdminCest.php:createUser
tests/functional/AdminCest.php:deleteUser
```
Tasks from `\Codeception\Task\SplitTestsByGroups` will generate non-intersecting group files. You can either split your tests by files or by single tests:
```php
<?php
function parallelSplitTests()
{
// Split your tests by files
$this->taskSplitTestFilesByGroups(5)
->projectRoot('.')
->testsFrom('tests/acceptance')
->groupsTo('tests/_data/paracept_')
->run();
/*
// Split your tests by single tests (alternatively)
$this->taskSplitTestsByGroups(5)
->projectRoot('.')
->testsFrom('tests/acceptance')
->groupsTo('tests/_data/paracept_')
->run();
*/
}
```
Let's prepare group files:
```bash
$ robo parallel:split-tests
[Codeception\Task\SplitTestFilesByGroupsTask] Processing 33 files
[Codeception\Task\SplitTestFilesByGroupsTask] Writing tests/_data/paracept_1
[Codeception\Task\SplitTestFilesByGroupsTask] Writing tests/_data/paracept_2
[Codeception\Task\SplitTestFilesByGroupsTask] Writing tests/_data/paracept_3
[Codeception\Task\SplitTestFilesByGroupsTask] Writing tests/_data/paracept_4
[Codeception\Task\SplitTestFilesByGroupsTask] Writing tests/_data/paracept_5
```
Now we have group files. We should update `codeception.yml` to load generated group files. In our case we have groups: *paracept_1*, *paracept_2*, *paracept_3*, *paracept_4*, *paracept_5*.
```yaml
groups:
paracept_*: tests/_data/paracept_*
```
Let's try to execute tests from the second group:
```bash
$ codecept run acceptance -g paracept_2
```
#### Step 2: Running Tests
Robo has `ParallelExec` task to spawn background processes.
##### Inside Container
If you are using [Docker](#docker) containers you can launch multiple Codeception containers for different groups:
```php
public function parallelRun()
{
$parallel = $this->taskParallelExec();
for ($i = 1; $i <= 5; $i++) {
$parallel->process(
$this->taskExec('docker-compose run --rm codecept run')
->opt('group', "paracept_$i") // run for groups paracept_*
->opt('xml', "tests/_log/result_$i.xml"); // provide xml report
);
}
return $parallel->run();
}
```
##### Locally
If you want to run tests locally just use preinstalled `taskCodecept` task of Robo to define Codeception commands and put them inside `parallelExec`.
```php
<?php
public function parallelRun()
{
$parallel = $this->taskParallelExec();
for ($i = 1; $i <= 5; $i++) {
$parallel->process(
$this->taskCodecept() // use built-in Codecept task
->suite('acceptance') // run acceptance tests
->group("paracept_$i") // for all paracept_* groups
->xml("tests/_log/result_$i.xml") // save XML results
);
}
return $parallel->run();
}
```
In case you don't use containers you can isolate processes by starting different web servers and databases per each test process.
We can define different databases for different processes. This can be done using [Environments](http://codeception.com/docs/07-AdvancedUsage#Environments). Let's define 5 new environments in `acceptance.suite.yml`:
```yaml
actor: AcceptanceTester
modules:
enabled:
- Db:
dsn: 'mysql:dbname=testdb;host=127.0.0.1'
user: 'root'
dump: 'tests/_data/dump.sql'
populate: true
cleanup: true
- WebDriver:
url: 'http://localhost/'
env:
env1:
modules:
config:
Db:
dsn: 'mysql:dbname=testdb_1;host=127.0.0.1'
WebDriver:
url: 'http://test1.localhost/'
env2:
modules:
config:
Db:
dsn: 'mysql:dbname=testdb_2;host=127.0.0.1'
WebDriver:
url: 'http://test2.localhost/'
env3:
modules:
config:
Db:
dsn: 'mysql:dbname=testdb_3;host=127.0.0.1'
WebDriver:
url: 'http://test3.localhost/'
env4:
modules:
config:
Db:
dsn: 'mysql:dbname=testdb_4;host=127.0.0.1'
WebDriver:
url: 'http://test4.localhost/'
env5:
modules:
config:
Db:
dsn: 'mysql:dbname=testdb_5;host=127.0.0.1'
WebDriver:
url: 'http://test5.localhost/'
```
----
After the `parallelRun` method is defined you can execute tests with
```bash
$ robo parallel:run
```
#### Step 3: Merge Results
In case of `parallelExec` task we recommend to save results as JUnit XML, which can be merged and plugged into Continuous Integration server.
```php
<?php
function parallelMergeResults()
{
$merge = $this->taskMergeXmlReports();
for ($i=1; $i<=5; $i++) {
$merge->from("tests/_output/result_paracept_$i.xml");
}
$merge->into("tests/_output/result_paracept.xml")->run();
}
```
Now, we can execute :
```bash
$ robo parallel:merge-results
```
`result_paracept.xml` file will be generated. It can be processed and analyzed.
#### All Together
To create one command to rule them all we can define new public method `parallelAll` and execute all commands. We will save the result of `parallelRun` and use it for our final exit code:
```php
<?php
function parallelAll()
{
$this->parallelSplitTests();
$result = $this->parallelRun();
$this->parallelMergeResults();
return $result;
}
```
## Conclusion
Codeception does not provide tools for parallel test execution. This is a complex task and solutions may vary depending on a project. We use [Robo](http://robo.li) task runner as an external tool to perform all required steps. To prepare our tests to be executed in parallel we use Codeception features of dynamic groups and environments. To do even more we can create Extensions and Group classes to perform dynamic configuration depending on a test process.

View File

@@ -0,0 +1,202 @@
# AMQP
This module interacts with message broker software that implements
the Advanced Message Queuing Protocol (AMQP) standard. For example, RabbitMQ (tested).
<div class="alert alert-info">
To use this module with Composer you need <em>"php-amqplib/php-amqplib": "~2.4"</em> package.
</div>
## Config
* host: localhost - host to connect
* username: guest - username to connect
* password: guest - password to connect
* vhost: '/' - vhost to connect
* cleanup: true - defined queues will be purged before running every test.
* queues: [mail, twitter] - queues to cleanup
* single_channel - create and use only one channel during test execution
### Example
modules:
enabled:
- AMQP:
host: 'localhost'
port: '5672'
username: 'guest'
password: 'guest'
vhost: '/'
queues: [queue1, queue2]
single_channel: false
## Public Properties
* connection - AMQPStreamConnection - current connection
## Actions
### bindQueueToExchange
Binds a queue to an exchange
This is an alias of method `queue_bind` of `PhpAmqpLib\Channel\AMQPChannel`.
```php
<?php
$I->bindQueueToExchange(
'nameOfMyQueueToBind', // name of the queue
'transactionTracking.transaction', // exchange name to bind to
'your.routing.key' // Optionally, provide a binding key
)
```
* `param string` $queue
* `param string` $exchange
* `param string` $routing_key
* `param bool` $nowait
* `param array` $arguments
* `param int` $ticket
* `return` mixed|null
### declareExchange
Declares an exchange
This is an alias of method `exchange_declare` of `PhpAmqpLib\Channel\AMQPChannel`.
```php
<?php
$I->declareExchange(
'nameOfMyExchange', // exchange name
'topic' // exchange type
)
```
* `param string` $exchange
* `param string` $type
* `param bool` $passive
* `param bool` $durable
* `param bool` $auto_delete
* `param bool` $internal
* `param bool` $nowait
* `param array` $arguments
* `param int` $ticket
* `return` mixed|null
### declareQueue
Declares queue, creates if needed
This is an alias of method `queue_declare` of `PhpAmqpLib\Channel\AMQPChannel`.
```php
<?php
$I->declareQueue(
'nameOfMyQueue', // exchange name
)
```
* `param string` $queue
* `param bool` $passive
* `param bool` $durable
* `param bool` $exclusive
* `param bool` $auto_delete
* `param bool` $nowait
* `param array` $arguments
* `param int` $ticket
* `return` mixed|null
### grabMessageFromQueue
Takes last message from queue.
``` php
<?php
$message = $I->grabMessageFromQueue('queue.emails');
?>
```
* `param string` $queue
* `return` \PhpAmqpLib\Message\AMQPMessage
### purgeAllQueues
Purge all queues defined in config.
``` php
<?php
$I->purgeAllQueues();
?>
```
### purgeQueue
Purge a specific queue defined in config.
``` php
<?php
$I->purgeQueue('queue.emails');
?>
```
* `param string` $queueName
### pushToExchange
Sends message to exchange by sending exchange name, message
and (optionally) a routing key
``` php
<?php
$I->pushToExchange('exchange.emails', 'thanks');
$I->pushToExchange('exchange.emails', new AMQPMessage('Thanks!'));
$I->pushToExchange('exchange.emails', new AMQPMessage('Thanks!'), 'severity');
?>
```
* `param string` $exchange
* `param string|\PhpAmqpLib\Message\AMQPMessage` $message
* `param string` $routing_key
### pushToQueue
Sends message to queue
``` php
<?php
$I->pushToQueue('queue.jobs', 'create user');
$I->pushToQueue('queue.jobs', new AMQPMessage('create'));
?>
```
* `param string` $queue
* `param string|\PhpAmqpLib\Message\AMQPMessage` $message
### seeMessageInQueueContainsText
Checks if message containing text received.
**This method drops message from queue**
**This method will wait for message. If none is sent the script will stuck**.
``` php
<?php
$I->pushToQueue('queue.emails', 'Hello, davert');
$I->seeMessageInQueueContainsText('queue.emails','davert');
?>
```
* `param string` $queue
* `param string` $text
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/AMQP.php">Help us to improve documentation. Edit module reference</a></div>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
# Apc
This module interacts with the [Alternative PHP Cache (APC)](http://php.net/manual/en/intro.apcu.php)
using either _APCu_ or _APC_ extension.
Performs a cleanup by flushing all values after each test run.
## Status
* Maintainer: **Serghei Iakovlev**
* Stability: **stable**
* Contact: serghei@phalconphp.com
### Example (`unit.suite.yml`)
```yaml
modules:
- Apc
```
Be sure you don't use the production server to connect.
## Actions
### dontSeeInApc
Checks item in APC(u) doesn't exist or is the same as expected.
Examples:
``` php
<?php
// With only one argument, only checks the key does not exist
$I->dontSeeInApc('users_count');
// Checks a 'users_count' exists does not exist or its value is not the one provided
$I->dontSeeInApc('users_count', 200);
?>
```
* `param string|string[]` $key
* `param mixed` $value
### flushApc
Clears the APC(u) cache
### grabValueFromApc
Grabs value from APC(u) by key.
Example:
``` php
<?php
$users_count = $I->grabValueFromApc('users_count');
?>
```
* `param string|string[]` $key
### haveInApc
Stores an item `$value` with `$key` on the APC(u).
Examples:
```php
<?php
// Array
$I->haveInApc('users', ['name' => 'miles', 'email' => 'miles@davis.com']);
// Object
$I->haveInApc('user', UserRepository::findFirst());
// Key as array of 'key => value'
$entries = [];
$entries['key1'] = 'value1';
$entries['key2'] = 'value2';
$entries['key3'] = ['value3a','value3b'];
$entries['key4'] = 4;
$I->haveInApc($entries, null);
?>
```
* `param string|array` $key
* `param mixed` $value
* `param int` $expiration
### seeInApc
Checks item in APC(u) exists and the same as expected.
Examples:
``` php
<?php
// With only one argument, only checks the key exists
$I->seeInApc('users_count');
// Checks a 'users_count' exists and has the value 200
$I->seeInApc('users_count', 200);
?>
```
* `param string|string[]` $key
* `param mixed` $value
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Apc.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,350 @@
# Asserts
Special module for using asserts in your tests.
## Actions
### assertArrayHasKey
* `param` $key
* `param` $actual
* `param` $description
### assertArrayNotHasKey
* `param` $key
* `param` $actual
* `param` $description
### assertArraySubset
Checks that array contains subset.
* `param array` $subset
* `param array` $array
* `param bool` $strict
* `param string` $message
### assertContains
Checks that haystack contains needle
* `param` $needle
* `param` $haystack
* `param string` $message
### assertCount
* `param` $expectedCount
* `param` $actual
* `param` $description
### assertEmpty
Checks that variable is empty.
* `param` $actual
* `param string` $message
### assertEquals
Checks that two variables are equal. If you're comparing floating-point values,
you can specify the optional "delta" parameter which dictates how great of a precision
error are you willing to tolerate in order to consider the two values equal.
Regular example:
```php
<?php
$I->assertEquals($element->getChildrenCount(), 5);
```
Floating-point example:
```php
<?php
$I->assertEquals($calculator->add(0.1, 0.2), 0.3, 'Calculator should add the two numbers correctly.', 0.01);
```
* `param` $expected
* `param` $actual
* `param string` $message
* `param float` $delta
### assertFalse
Checks that condition is negative.
* `param` $condition
* `param string` $message
### assertFileExists
Checks if file exists
* `param string` $filename
* `param string` $message
### assertFileNotExists
Checks if file doesn't exist
* `param string` $filename
* `param string` $message
### assertGreaterOrEquals
* `param` $expected
* `param` $actual
* `param` $description
### assertGreaterThan
Checks that actual is greater than expected
* `param` $expected
* `param` $actual
* `param string` $message
### assertGreaterThanOrEqual
Checks that actual is greater or equal than expected
* `param` $expected
* `param` $actual
* `param string` $message
### assertInstanceOf
* `param` $class
* `param` $actual
* `param` $description
### assertInternalType
* `param` $type
* `param` $actual
* `param` $description
### assertIsEmpty
* `param` $actual
* `param` $description
### assertLessOrEquals
* `param` $expected
* `param` $actual
* `param` $description
### assertLessThan
Checks that actual is less than expected
* `param` $expected
* `param` $actual
* `param string` $message
### assertLessThanOrEqual
Checks that actual is less or equal than expected
* `param` $expected
* `param` $actual
* `param string` $message
### assertNotContains
Checks that haystack doesn't contain needle.
* `param` $needle
* `param` $haystack
* `param string` $message
### assertNotEmpty
Checks that variable is not empty.
* `param` $actual
* `param string` $message
### assertNotEquals
Checks that two variables are not equal. If you're comparing floating-point values,
you can specify the optional "delta" parameter which dictates how great of a precision
error are you willing to tolerate in order to consider the two values not equal.
Regular example:
```php
<?php
$I->assertNotEquals($element->getChildrenCount(), 0);
```
Floating-point example:
```php
<?php
$I->assertNotEquals($calculator->add(0.1, 0.2), 0.4, 'Calculator should add the two numbers correctly.', 0.01);
```
* `param` $expected
* `param` $actual
* `param string` $message
* `param float` $delta
### assertNotFalse
Checks that the condition is NOT false (everything but false)
* `param` $condition
* `param string` $message
### assertNotInstanceOf
* `param` $class
* `param` $actual
* `param` $description
### assertNotNull
Checks that variable is not NULL
* `param` $actual
* `param string` $message
### assertNotRegExp
Checks that string not match with pattern
* `param string` $pattern
* `param string` $string
* `param string` $message
### assertNotSame
Checks that two variables are not same
* `param` $expected
* `param` $actual
* `param string` $message
### assertNotTrue
Checks that the condition is NOT true (everything but true)
* `param` $condition
* `param string` $message
### assertNull
Checks that variable is NULL
* `param` $actual
* `param string` $message
### assertRegExp
Checks that string match with pattern
* `param string` $pattern
* `param string` $string
* `param string` $message
### assertSame
Checks that two variables are same
* `param` $expected
* `param` $actual
* `param string` $message
### assertStringStartsNotWith
Checks that a string doesn't start with the given prefix.
* `param string` $prefix
* `param string` $string
* `param string` $message
### assertStringStartsWith
Checks that a string starts with the given prefix.
* `param string` $prefix
* `param string` $string
* `param string` $message
### assertTrue
Checks that condition is positive.
* `param` $condition
* `param string` $message
### expectException
Handles and checks exception called inside callback function.
Either exception class name or exception instance should be provided.
```php
<?php
$I->expectException(MyException::class, function() {
$this->doSomethingBad();
});
$I->expectException(new MyException(), function() {
$this->doSomethingBad();
});
```
If you want to check message or exception code, you can pass them with exception instance:
```php
<?php
// will check that exception MyException is thrown with "Don't do bad things" message
$I->expectException(new MyException("Don't do bad things"), function() {
$this->doSomethingBad();
});
```
* `param` $exception string or \Exception
* `param` $callback
### fail
Fails the test with message.
* `param` $message
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Asserts.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,75 @@
# Cli
Wrapper for basic shell commands and shell output
## Responsibility
* Maintainer: **davert**
* Status: **stable**
* Contact: codecept@davert.mail.ua
*Please review the code of non-stable modules and provide patches if you have issues.*
## Actions
### dontSeeInShellOutput
Checks that output from latest command doesn't contain text
* `param` $text
### runShellCommand
Executes a shell command.
Fails If exit code is > 0. You can disable this by setting second parameter to false
```php
<?php
$I->runShellCommand('phpunit');
// do not fail test when command fails
$I->runShellCommand('phpunit', false);
```
* `param` $command
* `param bool` $failNonZero
### seeInShellOutput
Checks that output from last executed command contains text
* `param` $text
### seeResultCodeIs
Checks result code
```php
<?php
$I->seeResultCodeIs(0);
```
* `param` $code
### seeResultCodeIsNot
Checks result code
```php
<?php
$I->seeResultCodeIsNot(0);
```
* `param` $code
### seeShellOutputMatches
* `param` $regex
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Cli.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,166 @@
# DataFactory
DataFactory allows you to easily generate and create test data using [**FactoryMuffin**](https://github.com/thephpleague/factory-muffin).
DataFactory uses an ORM of your application to define, save and cleanup data. Thus, should be used with ORM or Framework modules.
This module requires packages installed:
```json
{
"league/factory-muffin": "^3.0",
}
```
Generation rules can be defined in a factories file. You will need to create `factories.php` (it is recommended to store it in `_support` dir)
Follow [FactoryMuffin documentation](https://github.com/thephpleague/factory-muffin) to set valid rules.
Random data provided by [Faker](https://github.com/fzaninotto/Faker) library.
```php
<?php
use League\FactoryMuffin\Faker\Facade as Faker;
$fm->define(User::class)->setDefinitions([
'name' => Faker::name(),
// generate email
'email' => Faker::email(),
'body' => Faker::text(),
// generate a profile and return its Id
'profile_id' => 'factory|Profile'
]);
```
Configure this module to load factory definitions from a directory.
You should also specify a module with an ORM as a dependency.
```yaml
modules:
enabled:
- Yii2:
configFile: path/to/config.php
- DataFactory:
factories: tests/_support/factories
depends: Yii2
```
(you can also use Laravel5 and Phalcon).
In this example factories are loaded from `tests/_support/factories` directory. Please note that this directory is relative from the codeception.yml file (so for Yii2 it would be codeception/_support/factories).
You should create this directory manually and create PHP files in it with factories definitions following [official documentation](https://github.com/thephpleague/factory-muffin#usage).
In cases you want to use data from database inside your factory definitions you can define them in Helper.
For instance, if you use Doctrine, this allows you to access `EntityManager` inside a definition.
To proceed you should create Factories helper via `generate:helper` command and enable it:
```
modules:
enabled:
- DataFactory:
depends: Doctrine2
- \Helper\Factories
```
In this case you can define factories from a Helper class with `_define` method.
```php
<?php
public function _beforeSuite()
{
$factory = $this->getModule('DataFactory');
// let us get EntityManager from Doctrine
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(User::class, [
// generate random user name
// use League\FactoryMuffin\Faker\Facade as Faker;
'name' => Faker::name(),
// get real company from database
'company' => $em->getRepository(Company::class)->find(),
// let's generate a profile for each created user
// receive an entity and set it via `setProfile` method
// UserProfile factory should be defined as well
'profile' => 'entity|'.UserProfile::class
]);
}
```
Factory Definitions are described in official [Factory Muffin Documentation](https://github.com/thephpleague/factory-muffin)
### Related Models Generators
If your module relies on other model you can generate them both.
To create a related module you can use either `factory` or `entity` prefix, depending on ORM you use.
In case your ORM expects an Id of a related record (Eloquent) to be set use `factory` prefix:
```php
'user_id' => 'factory|User'
```
In case your ORM expects a related record itself (Doctrine) then you should use `entity` prefix:
```php
'user' => 'entity|User'
```
## Actions
### have
Generates and saves a record,.
```php
$I->have('User'); // creates user
$I->have('User', ['is_active' => true]); // creates active user
```
Returns an instance of created user.
* `param string` $name
* `param array` $extraAttrs
* `return` object
### haveMultiple
Generates and saves a record multiple times.
```php
$I->haveMultiple('User', 10); // create 10 users
$I->haveMultiple('User', 10, ['is_active' => true]); // create 10 active users
```
* `param string` $name
* `param int` $times
* `param array` $extraAttrs
* `return` \object[]
### make
Generates a record instance.
This does not save it in the database. Use `have` for that.
```php
$user = $I->make('User'); // return User instance
$activeUser = $I->make('User', ['is_active' => true]); // return active user instance
```
Returns an instance of created user without creating a record in database.
* `param string` $name
* `param array` $extraAttrs
* `return` object
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/DataFactory.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,327 @@
# Db
Access a database.
The most important function of this module is to clean a database before each test.
This module also provides actions to perform checks in a database, e.g. [seeInDatabase()](http://codeception.com/docs/modules/Db#seeInDatabase)
In order to have your database populated with data you need a raw SQL dump.
Simply put the dump in the `tests/_data` directory (by default) and specify the path in the config.
The next time after the database is cleared, all your data will be restored from the dump.
Don't forget to include `CREATE TABLE` statements in the dump.
Supported and tested databases are:
* MySQL
* SQLite (i.e. just one file)
* PostgreSQL
Also available:
* MS SQL
* Oracle
Connection is done by database Drivers, which are stored in the `Codeception\Lib\Driver` namespace.
[Check out the drivers](https://github.com/Codeception/Codeception/tree/2.3/src/Codeception/Lib/Driver)
if you run into problems loading dumps and cleaning databases.
## Config
* dsn *required* - PDO DSN
* user *required* - username to access database
* password *required* - password
* dump - path to database dump
* populate: false - whether the the dump should be loaded before the test suite is started
* cleanup: false - whether the dump should be reloaded before each test
* reconnect: false - whether the module should reconnect to the database before each test
* waitlock: 0 - wait lock (in seconds) that the database session should use for DDL statements
* ssl_key - path to the SSL key (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-key)
* ssl_cert - path to the SSL certificate (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-cert)
* ssl_ca - path to the SSL certificate authority (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-ca)
* ssl_verify_server_cert - disables certificate CN verification (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php)
* ssl_cipher - list of one or more permissible ciphers to use for SSL encryption (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-cipher)
## Example
modules:
enabled:
- Db:
dsn: 'mysql:host=localhost;dbname=testdb'
user: 'root'
password: ''
dump: 'tests/_data/dump.sql'
populate: true
cleanup: true
reconnect: true
waitlock: 10
ssl_key: '/path/to/client-key.pem'
ssl_cert: '/path/to/client-cert.pem'
ssl_ca: '/path/to/ca-cert.pem'
ssl_verify_server_cert: false
ssl_cipher: 'AES256-SHA'
## SQL data dump
There are two ways of loading the dump into your database:
### Populator
The recommended approach is to configure a `populator`, an external command to load a dump. Command parameters like host, username, password, database
can be obtained from the config and inserted into placeholders:
For MySQL:
```yaml
modules:
enabled:
- Db:
dsn: 'mysql:host=localhost;dbname=testdb'
user: 'root'
password: ''
dump: 'tests/_data/dump.sql'
populate: true # run populator before all tests
cleanup: true # run populator before each test
populator: 'mysql -u $user -h $host $dbname < $dump'
```
For PostgreSQL (using pg_restore)
```
modules:
enabled:
- Db:
dsn: 'pgsql:host=localhost;dbname=testdb'
user: 'root'
password: ''
dump: 'tests/_data/db_backup.dump'
populate: true # run populator before all tests
cleanup: true # run populator before each test
populator: 'pg_restore -u $user -h $host -D $dbname < $dump'
```
Variable names are being taken from config and DSN which has a `keyword=value` format, so you should expect to have a variable named as the
keyword with the full value inside it.
PDO dsn elements for the supported drivers:
* MySQL: [PDO_MYSQL DSN](https://secure.php.net/manual/en/ref.pdo-mysql.connection.php)
* SQLite: [PDO_SQLITE DSN](https://secure.php.net/manual/en/ref.pdo-sqlite.connection.php)
* PostgreSQL: [PDO_PGSQL DSN](https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php)
* MSSQL: [PDO_SQLSRV DSN](https://secure.php.net/manual/en/ref.pdo-sqlsrv.connection.php)
* Oracle: [PDO_OCI DSN](https://secure.php.net/manual/en/ref.pdo-oci.connection.php)
### Dump
Db module by itself can load SQL dump without external tools by using current database connection.
This approach is system-independent, however, it is slower than using a populator and may have parsing issues (see below).
Provide a path to SQL file in `dump` config option:
```yaml
modules:
enabled:
- Db:
dsn: 'mysql:host=localhost;dbname=testdb'
user: 'root'
password: ''
populate: true # load dump before all tests
cleanup: true # load dump for each test
dump: 'tests/_data/dump.sql'
```
To parse SQL Db file, it should follow this specification:
* Comments are permitted.
* The `dump.sql` may contain multiline statements.
* The delimiter, a semi-colon in this case, must be on the same line as the last statement:
```sql
-- Add a few contacts to the table.
REPLACE INTO `Contacts` (`created`, `modified`, `status`, `contact`, `first`, `last`) VALUES
(NOW(), NOW(), 1, 'Bob Ross', 'Bob', 'Ross'),
(NOW(), NOW(), 1, 'Fred Flintstone', 'Fred', 'Flintstone');
-- Remove existing orders for testing.
DELETE FROM `Order`;
```
## Query generation
`seeInDatabase`, `dontSeeInDatabase`, `seeNumRecords`, `grabFromDatabase` and `grabNumRecords` methods
accept arrays as criteria. WHERE condition is generated using item key as a field name and
item value as a field value.
Example:
```php
<?php
$I->seeInDatabase('users', array('name' => 'Davert', 'email' => 'davert@mail.com'));
```
Will generate:
```sql
SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` = 'davert@mail.com'
```
Since version 2.1.9 it's possible to use LIKE in a condition, as shown here:
```php
<?php
$I->seeInDatabase('users', array('name' => 'Davert', 'email like' => 'davert%'));
```
Will generate:
```sql
SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` LIKE 'davert%'
```
## Public Properties
* dbh - contains the PDO connection
* driver - contains the Connection Driver
## Actions
### dontSeeInDatabase
Effect is opposite to ->seeInDatabase
Asserts that there is no record with the given column values in a database.
Provide table name and column values.
``` php
<?php
$I->dontSeeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']);
```
Fails if such user was found.
Comparison expressions can be used as well:
```php
<?php
$I->dontSeeInDatabase('posts', ['num_comments >=' => '0']);
$I->dontSeeInDatabase('users', ['email like' => 'miles%']);
```
Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`.
* `param string` $table
* `param array` $criteria
### grabColumnFromDatabase
Fetches all values from the column in database.
Provide table name, desired column and criteria.
``` php
<?php
$mails = $I->grabColumnFromDatabase('users', 'email', array('name' => 'RebOOter'));
```
* `param string` $table
* `param string` $column
* `param array` $criteria
* `return` array
### grabFromDatabase
Fetches all values from the column in database.
Provide table name, desired column and criteria.
``` php
<?php
$mails = $I->grabFromDatabase('users', 'email', array('name' => 'RebOOter'));
```
* `param string` $table
* `param string` $column
* `param array` $criteria
* `return` array
### grabNumRecords
Returns the number of rows in a database
* `param string` $table Table name
* `param array` $criteria Search criteria [Optional]
* `return` int
### haveInDatabase
Inserts an SQL record into a database. This record will be erased after the test.
```php
<?php
$I->haveInDatabase('users', array('name' => 'miles', 'email' => 'miles@davis.com'));
?>
```
* `param string` $table
* `param array` $data
* `return integer` $id
### isPopulated
__not documented__
### seeInDatabase
Asserts that a row with the given column values exists.
Provide table name and column values.
```php
<?php
$I->seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']);
```
Fails if no such user found.
Comparison expressions can be used as well:
```php
<?php
$I->seeInDatabase('posts', ['num_comments >=' => '0']);
$I->seeInDatabase('users', ['email like' => 'miles@davis.com']);
```
Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`.
* `param string` $table
* `param array` $criteria
### seeNumRecords
Asserts that the given number of records were found in the database.
```php
<?php
$I->seeNumRecords(1, 'users', ['name' => 'davert'])
?>
```
* `param int` $expectedNumber Expected number
* `param string` $table Table name
* `param array` $criteria Search criteria [Optional]
### updateInDatabase
Update an SQL record into a database.
```php
<?php
$I->updateInDatabase('users', array('isAdmin' => true), array('email' => 'miles@davis.com'));
?>
```
* `param string` $table
* `param array` $data
* `param array` $criteria
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Db.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,193 @@
# Doctrine2
Access the database using [Doctrine2 ORM](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/).
When used with Zend Framework 2 or Symfony2, Doctrine's Entity Manager is automatically retrieved from Service Locator.
Set up your `functional.suite.yml` like this:
```
modules:
enabled:
- Symfony # 'ZF2' or 'Symfony'
- Doctrine2:
depends: Symfony
cleanup: true # All doctrine queries will be wrapped in a transaction, which will be rolled back at the end of each test
```
If you don't use Symfony or Zend Framework, you need to specify a callback function to retrieve the Entity Manager:
```
modules:
enabled:
- Doctrine2:
connection_callback: ['MyDb', 'createEntityManager']
cleanup: true # All doctrine queries will be wrapped in a transaction, which will be rolled back at the end of each test
```
This will use static method of `MyDb::createEntityManager()` to establish the Entity Manager.
By default, the module will wrap everything into a transaction for each test and roll it back afterwards. By doing this
tests will run much faster and will be isolated from each other.
## Status
* Maintainer: **davert**
* Stability: **stable**
* Contact: codecept@davert.mail.ua
## Config
## Public Properties
* `em` - Entity Manager
## Actions
### dontSeeInRepository
Flushes changes to database and performs `findOneBy()` call for current repository.
* `param` $entity
* `param array` $params
### flushToDatabase
Performs $em->flush();
### grabEntitiesFromRepository
Selects entities from repository.
It builds query based on array of parameters.
You can use entity associations to build complex queries.
Example:
``` php
<?php
$users = $I->grabEntitiesFromRepository('AppBundle:User', array('name' => 'davert'));
?>
```
* `Available since` 1.1
* `param` $entity
* `param array` $params
* `return` array
### grabEntityFromRepository
Selects a single entity from repository.
It builds query based on array of parameters.
You can use entity associations to build complex queries.
Example:
``` php
<?php
$user = $I->grabEntityFromRepository('User', array('id' => '1234'));
?>
```
* `Available since` 1.1
* `param` $entity
* `param array` $params
* `return` object
### grabFromRepository
Selects field value from repository.
It builds query based on array of parameters.
You can use entity associations to build complex queries.
Example:
``` php
<?php
$email = $I->grabFromRepository('User', 'email', array('name' => 'davert'));
?>
```
* `Available since` 1.1
* `param` $entity
* `param` $field
* `param array` $params
* `return` array
### haveFakeRepository
Mocks the repository.
With this action you can redefine any method of any repository.
Please, note: this fake repositories will be accessible through entity manager till the end of test.
Example:
``` php
<?php
$I->haveFakeRepository('Entity\User', array('findByUsername' => function($username) { return null; }));
```
This creates a stub class for Entity\User repository with redefined method findByUsername,
which will always return the NULL value.
* `param` $classname
* `param array` $methods
### haveInRepository
Persists record into repository.
This method creates an entity, and sets its properties directly (via reflection).
Setters of entity won't be executed, but you can create almost any entity and save it to database.
Returns id using `getId` of newly created entity.
```php
$I->haveInRepository('Entity\User', array('name' => 'davert'));
```
### persistEntity
Adds entity to repository and flushes. You can redefine it's properties with the second parameter.
Example:
``` php
<?php
$I->persistEntity(new \Entity\User, array('name' => 'Miles'));
$I->persistEntity($user, array('name' => 'Miles'));
```
* `param` $obj
* `param array` $values
### seeInRepository
Flushes changes to database, and executes a query with parameters defined in an array.
You can use entity associations to build complex queries.
Example:
``` php
<?php
$I->seeInRepository('AppBundle:User', array('name' => 'davert'));
$I->seeInRepository('User', array('name' => 'davert', 'Company' => array('name' => 'Codegyre')));
$I->seeInRepository('Client', array('User' => array('Company' => array('name' => 'Codegyre')));
?>
```
Fails if record for given criteria can\'t be found,
* `param` $entity
* `param array` $params
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Doctrine2.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,429 @@
# FTP
Works with SFTP/FTP servers.
In order to test the contents of a specific file stored on any remote FTP/SFTP system
this module downloads a temporary file to the local system. The temporary directory is
defined by default as ```tests/_data``` to specify a different directory set the tmp config
option to your chosen path.
Don't forget to create the folder and ensure its writable.
Supported and tested FTP types are:
* FTP
* SFTP
Connection uses php build in FTP client for FTP,
connection to SFTP uses [phpseclib](http://phpseclib.sourceforge.net/) pulled in using composer.
For SFTP, add [phpseclib](http://phpseclib.sourceforge.net/) to require list.
```
"require": {
"phpseclib/phpseclib": "0.3.6"
}
```
## Status
* Maintainer: **nathanmac**
* Stability:
- FTP: **stable**
- SFTP: **stable**
* Contact: nathan.macnamara@outlook.com
## Config
* type: ftp - type of connection ftp/sftp (defaults to ftp).
* host *required* - hostname/ip address of the ftp server.
* port: 21 - port number for the ftp server
* timeout: 90 - timeout settings for connecting the ftp server.
* user: anonymous - user to access ftp server, defaults to anonymous authentication.
* password - password, defaults to empty for anonymous.
* key - path to RSA key for sftp.
* tmp - path to local directory for storing tmp files.
* passive: true - Turns on or off passive mode (FTP only)
* cleanup: true - remove tmp files from local directory on completion.
### Example
#### Example (FTP)
modules:
enabled: [FTP]
config:
FTP:
type: ftp
host: '127.0.0.1'
port: 21
timeout: 120
user: 'root'
password: 'root'
key: ~/.ssh/id_rsa
tmp: 'tests/_data/ftp'
passive: true
cleanup: false
#### Example (SFTP)
modules:
enabled: [FTP]
config:
FTP:
type: sftp
host: '127.0.0.1'
port: 22
timeout: 120
user: 'root'
password: 'root'
key: ''
tmp: 'tests/_data/ftp'
cleanup: false
This module extends the Filesystem module, file contents methods are inherited from this module.
## Actions
### amInPath
Enters a directory on the ftp system - FTP root directory is used by default
* `param` $path
### cleanDir
Erases directory contents on the FTP/SFTP server
``` php
<?php
$I->cleanDir('logs');
?>
```
* `param` $dirname
### copyDir
Currently not supported in this module, overwrite inherited method
* `param` $src
* `param` $dst
### deleteDir
Deletes directory with all subdirectories on the remote FTP/SFTP server
``` php
<?php
$I->deleteDir('vendor');
?>
```
* `param` $dirname
### deleteFile
Deletes a file on the remote FTP/SFTP system
``` php
<?php
$I->deleteFile('composer.lock');
?>
```
* `param` $filename
### deleteThisFile
Deletes a file
### dontSeeFileFound
Checks if file does not exist in path on the remote FTP/SFTP system
* `param` $filename
* `param string` $path
### dontSeeFileFoundMatches
Checks if file does not exist in path on the remote FTP/SFTP system, using regular expression as filename.
DOES NOT OPEN the file when it's exists
* `param` $regex
* `param string` $path
### dontSeeInThisFile
Checks If opened file doesn't contain `text` in it
``` php
<?php
$I->openFile('composer.json');
$I->dontSeeInThisFile('codeception/codeception');
?>
```
* `param string` $text
### grabDirectory
Grabber method to return current working directory
```php
<?php
$pwd = $I->grabDirectory();
?>
```
* `return` string
### grabFileCount
Grabber method for returning file/folders count in directory
```php
<?php
$count = $I->grabFileCount();
$count = $I->grabFileCount('TEST', false); // Include . .. .thumbs.db
?>
```
* `param string` $path
* `param bool` $ignore - suppress '.', '..' and '.thumbs.db'
* `return` int
### grabFileList
Grabber method for returning file/folders listing in an array
```php
<?php
$files = $I->grabFileList();
$count = $I->grabFileList('TEST', false); // Include . .. .thumbs.db
?>
```
* `param string` $path
* `param bool` $ignore - suppress '.', '..' and '.thumbs.db'
* `return` array
### grabFileModified
Grabber method to return last modified timestamp
```php
<?php
$time = $I->grabFileModified('test.txt');
?>
```
* `param` $filename
* `return` bool
### grabFileSize
Grabber method to return file size
```php
<?php
$size = $I->grabFileSize('test.txt');
?>
```
* `param` $filename
* `return` bool
### loginAs
Change the logged in user mid-way through your test, this closes the
current connection to the server and initialises and new connection.
On initiation of this modules you are automatically logged into
the server using the specified config options or defaulted
to anonymous user if not provided.
``` php
<?php
$I->loginAs('user','password');
?>
```
* `param String` $user
* `param String` $password
### makeDir
Create a directory on the server
``` php
<?php
$I->makeDir('vendor');
?>
```
* `param` $dirname
### openFile
Opens a file (downloads from the remote FTP/SFTP system to a tmp directory for processing)
and stores it's content.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeInThisFile('codeception/codeception');
?>
```
* `param` $filename
### renameDir
Rename/Move directory on the FTP/SFTP server
``` php
<?php
$I->renameDir('vendor', 'vendor_old');
?>
```
* `param` $dirname
* `param` $rename
### renameFile
Rename/Move file on the FTP/SFTP server
``` php
<?php
$I->renameFile('composer.lock', 'composer_old.lock');
?>
```
* `param` $filename
* `param` $rename
### seeFileContentsEqual
Checks the strict matching of file contents.
Unlike `seeInThisFile` will fail if file has something more than expected lines.
Better to use with HEREDOC strings.
Matching is done after removing "\r" chars from file content.
``` php
<?php
$I->openFile('process.pid');
$I->seeFileContentsEqual('3192');
?>
```
* `param string` $text
### seeFileFound
Checks if file exists in path on the remote FTP/SFTP system.
DOES NOT OPEN the file when it's exists
``` php
<?php
$I->seeFileFound('UserModel.php','app/models');
?>
```
* `param` $filename
* `param string` $path
### seeFileFoundMatches
Checks if file exists in path on the remote FTP/SFTP system, using regular expression as filename.
DOES NOT OPEN the file when it's exists
``` php
<?php
$I->seeFileFoundMatches('/^UserModel_([0-9]{6}).php$/','app/models');
?>
```
* `param` $regex
* `param string` $path
### seeInThisFile
Checks If opened file has `text` in it.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeInThisFile('codeception/codeception');
?>
```
* `param string` $text
### seeNumberNewLines
Checks If opened file has the `number` of new lines.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeNumberNewLines(5);
?>
```
* `param int` $number New lines
### seeThisFileMatches
Checks that contents of currently opened file matches $regex
* `param string` $regex
### writeToFile
Saves contents to tmp file and uploads the FTP/SFTP system.
Overwrites current file on server if exists.
``` php
<?php
$I->writeToFile('composer.json', 'some data here');
?>
```
* `param` $filename
* `param` $contents
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/FTP.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,153 @@
# Facebook
Provides testing for projects integrated with Facebook API.
Relies on Facebook's tool Test User API.
<div class="alert alert-info">
To use this module with Composer you need <em>"facebook/php-sdk4": "5.*"</em> package.
</div>
## Status
[ ![Facebook Status for Codeception/Codeception](https://codeship.com/projects/e4bc90d0-1ed5-0134-566c-1ed679ae6c9d/status?branch=2.2)](https://codeship.com/projects/160201)
* Stability: **beta**
* Maintainer: **tiger-seo**
* Contact: tiger.seo@codeception.com
## Config
* app_id *required* - Facebook application ID
* secret *required* - Facebook application secret
* test_user - Facebook test user parameters:
* name - You can specify a name for the test user you create. The specified name will also be used in the email address assigned to the test user.
* locale - You can specify a locale for the test user you create, the default is en_US. The list of supported locales is available at https://www.facebook.com/translations/FacebookLocales.xml
* permissions - An array of permissions. Your app is granted these permissions for the new test user. The full list of permissions is available at https://developers.facebook.com/docs/authentication/permissions
### Config example
modules:
enabled:
- Facebook:
depends: PhpBrowser
app_id: 412345678901234
secret: ccb79c1b0fdff54e4f7c928bf233aea5
test_user:
name: FacebookGuy
locale: uk_UA
permissions: [email, publish_stream]
### Test example:
``` php
<?php
$I = new ApiGuy($scenario);
$I->am('Guest');
$I->wantToTest('check-in to a place be published on the Facebook using API');
$I->haveFacebookTestUserAccount();
$accessToken = $I->grabFacebookTestUserAccessToken();
$I->haveHttpHeader('Auth', 'FacebookToken ' . $accessToken);
$I->amGoingTo('send request to the backend, so that it will publish on user\'s wall on Facebook');
$I->sendPOST('/api/v1/some-api-endpoint');
$I->seePostOnFacebookWithAttachedPlace('167724369950862');
```
``` php
<?php
$I = new WebGuy($scenario);
$I->am('Guest');
$I->wantToTest('log in to site using Facebook');
$I->haveFacebookTestUserAccount(); // create facebook test user
$I->haveTestUserLoggedInOnFacebook(); // so that facebook will not ask us for login and password
$fbUserFirstName = $I->grabFacebookTestUserFirstName();
$I->amOnPage('/welcome');
$I->see('Welcome, Guest');
$I->click('Login with Facebook');
$I->see('Welcome, ' . $fbUserFirstName);
```
@since 1.6.3
@author tiger.seo@gmail.com
## Actions
### grabFacebookTestUserAccessToken
Returns the test user access token.
* `return` string
### grabFacebookTestUserEmail
Returns the test user email.
* `return` string
### grabFacebookTestUserId
Returns the test user id.
* `return` string
### grabFacebookTestUserLoginUrl
Returns URL for test user auto-login.
* `return` string
### grabFacebookTestUserName
Returns the test user name.
* `return` string
### grabFacebookTestUserPassword
__not documented__
### haveFacebookTestUserAccount
Get facebook test user be created.
*Please, note that the test user is created only at first invoke, unless $renew arguments is true.*
* `param bool` $renew true if the test user should be recreated
### haveTestUserLoggedInOnFacebook
Get facebook test user be logged in on facebook.
This is done by going to facebook.com
@throws ModuleConfigException
### postToFacebookAsTestUser
Please, note that you must have publish_actions permission to be able to publish to user's feed.
* `param array` $params
### seePostOnFacebookWithAttachedPlace
Please, note that you must have publish_actions permission to be able to publish to user's feed.
* `param string` $placeId Place identifier to be verified against user published posts
### seePostOnFacebookWithMessage
Please, note that you must have publish_actions permission to be able to publish to user's feed.
* `param string` $message published post to be verified against the actual post on facebook
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Facebook.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,199 @@
# Filesystem
Module for testing local filesystem.
Fork it to extend the module for FTP, Amazon S3, others.
## Status
* Maintainer: **davert**
* Stability: **stable**
* Contact: codecept@davert.mail.ua
Module was developed to test Codeception itself.
## Actions
### amInPath
Enters a directory In local filesystem.
Project root directory is used by default
* `param string` $path
### cleanDir
Erases directory contents
``` php
<?php
$I->cleanDir('logs');
?>
```
* `param string` $dirname
### copyDir
Copies directory with all contents
``` php
<?php
$I->copyDir('vendor','old_vendor');
?>
```
* `param string` $src
* `param string` $dst
### deleteDir
Deletes directory with all subdirectories
``` php
<?php
$I->deleteDir('vendor');
?>
```
* `param string` $dirname
### deleteFile
Deletes a file
``` php
<?php
$I->deleteFile('composer.lock');
?>
```
* `param string` $filename
### deleteThisFile
Deletes a file
### dontSeeFileFound
Checks if file does not exist in path
* `param string` $filename
* `param string` $path
### dontSeeInThisFile
Checks If opened file doesn't contain `text` in it
``` php
<?php
$I->openFile('composer.json');
$I->dontSeeInThisFile('codeception/codeception');
?>
```
* `param string` $text
### openFile
Opens a file and stores it's content.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeInThisFile('codeception/codeception');
?>
```
* `param string` $filename
### seeFileContentsEqual
Checks the strict matching of file contents.
Unlike `seeInThisFile` will fail if file has something more than expected lines.
Better to use with HEREDOC strings.
Matching is done after removing "\r" chars from file content.
``` php
<?php
$I->openFile('process.pid');
$I->seeFileContentsEqual('3192');
?>
```
* `param string` $text
### seeFileFound
Checks if file exists in path.
Opens a file when it's exists
``` php
<?php
$I->seeFileFound('UserModel.php','app/models');
?>
```
* `param string` $filename
* `param string` $path
### seeInThisFile
Checks If opened file has `text` in it.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeInThisFile('codeception/codeception');
?>
```
* `param string` $text
### seeNumberNewLines
Checks If opened file has the `number` of new lines.
Usage:
``` php
<?php
$I->openFile('composer.json');
$I->seeNumberNewLines(5);
?>
```
* `param int` $number New lines
### seeThisFileMatches
Checks that contents of currently opened file matches $regex
* `param string` $regex
### writeToFile
Saves contents to file
* `param string` $filename
* `param string` $contents
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Filesystem.php">Help us to improve documentation. Edit module reference</a></div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
# Memcache
Connects to [memcached](http://www.memcached.org/) using either _Memcache_ or _Memcached_ extension.
Performs a cleanup by flushing all values after each test run.
## Status
* Maintainer: **davert**
* Stability: **beta**
* Contact: davert@codeception.com
## Configuration
* **`host`** (`string`, default `'localhost'`) - The memcached host
* **`port`** (`int`, default `11211`) - The memcached port
### Example (`unit.suite.yml`)
```yaml
modules:
- Memcache:
host: 'localhost'
port: 11211
```
Be sure you don't use the production server to connect.
## Public Properties
* **memcache** - instance of _Memcache_ or _Memcached_ object
## Actions
### clearMemcache
Flushes all Memcached data.
### dontSeeInMemcached
Checks item in Memcached doesn't exist or is the same as expected.
Examples:
``` php
<?php
// With only one argument, only checks the key does not exist
$I->dontSeeInMemcached('users_count');
// Checks a 'users_count' exists does not exist or its value is not the one provided
$I->dontSeeInMemcached('users_count', 200);
?>
```
* `param` $key
* `param` $value
### grabValueFromMemcached
Grabs value from memcached by key.
Example:
``` php
<?php
$users_count = $I->grabValueFromMemcached('users_count');
?>
```
* `param` $key
* `return` array|string
### haveInMemcached
Stores an item `$value` with `$key` on the Memcached server.
* `param string` $key
* `param mixed` $value
* `param int` $expiration
### seeInMemcached
Checks item in Memcached exists and the same as expected.
Examples:
``` php
<?php
// With only one argument, only checks the key exists
$I->seeInMemcached('users_count');
// Checks a 'users_count' exists and has the value 200
$I->seeInMemcached('users_count', 200);
?>
```
* `param` $key
* `param` $value
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Memcache.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,170 @@
# MongoDb
Works with MongoDb database.
The most important function of this module is cleaning database before each test.
To have your database properly cleaned you should configure it to access the database.
In order to have your database populated with data you need a valid js file with data (of the same style which can be fed up to mongo binary)
File can be generated by RockMongo export command
You can also use directory, generated by ```mongodump``` tool or it's ```.tar.gz``` archive (not available for Windows systems), generated by ```tar -czf <archive_file_name>.tar.gz <path_to dump directory>```.
Just put it in ``` tests/_data ``` dir (by default) and specify path to it in config.
Next time after database is cleared all your data will be restored from dump.
The DB preparation should as following:
- clean database
- system collection system.users should contain the user which will be authenticated while script performs DB operations
Connection is done by MongoDb driver, which is stored in Codeception\Lib\Driver namespace.
Check out the driver if you get problems loading dumps and cleaning databases.
HINT: This module can be used with [Mongofill](https://github.com/mongofill/mongofill) library which is Mongo client written in PHP without extension.
## Status
* Maintainer: **judgedim**, **davert**
* Stability: **beta**
* Contact: davert@codeception.com
*Please review the code of non-stable modules and provide patches if you have issues.*
## Config
* dsn *required* - MongoDb DSN with the db name specified at the end of the host after slash
* user *required* - user to access database
* password *required* - password
* dump_type *required* - type of dump.
One of 'js' (MongoDb::DUMP_TYPE_JS), 'mongodump' (MongoDb::DUMP_TYPE_MONGODUMP) or 'mongodump-tar-gz' (MongoDb::DUMP_TYPE_MONGODUMP_TAR_GZ).
default: MongoDb::DUMP_TYPE_JS).
* dump - path to database dump
* populate: true - should the dump be loaded before test suite is started.
* cleanup: true - should the dump be reloaded after each test
## Actions
### dontSeeInCollection
Checks if collection doesn't contain an item.
``` php
<?php
$I->dontSeeInCollection('users', array('name' => 'miles'));
```
* `param` $collection
* `param array` $criteria
### grabCollectionCount
Grabs the documents count from a collection
``` php
<?php
$count = $I->grabCollectionCount('users');
// or
$count = $I->grabCollectionCount('users', array('isAdmin' => true));
```
* `param` $collection
* `param array` $criteria
* `return` integer
### grabFromCollection
Grabs a data from collection
``` php
<?php
$user = $I->grabFromCollection('users', array('name' => 'miles'));
```
* `param` $collection
* `param array` $criteria
* `return` array
### haveInCollection
Inserts data into collection
``` php
<?php
$I->haveInCollection('users', array('name' => 'John', 'email' => 'john@coltrane.com'));
$user_id = $I->haveInCollection('users', array('email' => 'john@coltrane.com'));
```
* `param` $collection
* `param array` $data
### seeElementIsArray
Asserts that an element in a collection exists and is an Array
``` php
<?php
$I->seeElementIsArray('users', array('name' => 'John Doe') , 'data.skills');
```
* `param String` $collection
* `param Array` $criteria
* `param String` $elementToCheck
### seeElementIsObject
Asserts that an element in a collection exists and is an Object
``` php
<?php
$I->seeElementIsObject('users', array('name' => 'John Doe') , 'data');
```
* `param String` $collection
* `param Array` $criteria
* `param String` $elementToCheck
### seeInCollection
Checks if collection contains an item.
``` php
<?php
$I->seeInCollection('users', array('name' => 'miles'));
```
* `param` $collection
* `param array` $criteria
### seeNumElementsInCollection
Count number of records in a collection
``` php
<?php
$I->seeNumElementsInCollection('users', 2);
$I->seeNumElementsInCollection('users', 1, array('name' => 'miles'));
```
* `param` $collection
* `param integer` $expected
* `param array` $criteria
### useDatabase
Specify the database to use
``` php
<?php
$I->useDatabase('db_1');
```
* `param` $dbName
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/MongoDb.php">Help us to improve documentation. Edit module reference</a></div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,297 @@
# Queue
Works with Queue servers.
Testing with a selection of remote/local queueing services, including Amazon's SQS service
Iron.io service and beanstalkd service.
Supported and tested queue types are:
* [Iron.io](http://iron.io/)
* [Beanstalkd](http://kr.github.io/beanstalkd/)
* [Amazon SQS](http://aws.amazon.com/sqs/)
The following dependencies are needed for the listed queue servers:
* Beanstalkd: pda/pheanstalk ~3.0
* Amazon SQS: aws/aws-sdk-php
* IronMQ: iron-io/iron_mq
## Status
* Maintainer: **nathanmac**
* Stability:
- Iron.io: **stable**
- Beanstalkd: **stable**
- Amazon SQS: **stable**
* Contact: nathan.macnamara@outlook.com
## Config
The configuration settings depending on which queueing service is being used, all the options are listed
here. Refer to the configuration examples below to identify the configuration options required for your chosen
service.
* type - type of queueing server (defaults to beanstalkd).
* host - hostname/ip address of the queue server or the host for the iron.io when using iron.io service.
* port: 11300 - port number for the queue server.
* timeout: 90 - timeout settings for connecting the queue server.
* token - Iron.io access token.
* project - Iron.io project ID.
* key - AWS access key ID.
* version - AWS version (e.g. latest)
* endpoint - The full URI of the webservice. This is only required when connecting to a custom endpoint (e.g., a local version of SQS).
* secret - AWS secret access key.
Warning:
Hard-coding your credentials can be dangerous, because it is easy to accidentally commit your credentials
into an SCM repository, potentially exposing your credentials to more people than intended.
It can also make it difficult to rotate credentials in the future.
* profile - AWS credential profile
- it should be located in ~/.aws/credentials file
- eg: [default]
aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY
[project1]
aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY
- Note: Using IAM roles is the preferred technique for providing credentials
to applications running on Amazon EC2
http://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html?highlight=credentials
* region - A region parameter is also required for AWS, refer to the AWS documentation for possible values list.
### Example
#### Example (beanstalkd)
modules:
enabled: [Queue]
config:
Queue:
type: 'beanstalkd'
host: '127.0.0.1'
port: 11300
timeout: 120
#### Example (Iron.io)
modules:
enabled: [Queue]
config:
Queue:
'type': 'iron',
'host': 'mq-aws-us-east-1.iron.io',
'token': 'your-token',
'project': 'your-project-id'
#### Example (AWS SQS)
modules:
enabled: [Queue]
config:
Queue:
'type': 'aws',
'key': 'your-public-key',
'secret': 'your-secret-key',
'region': 'us-west-2'
#### Example AWS SQS using profile credentials
modules:
enabled: [Queue]
config:
Queue:
'type': 'aws',
'profile': 'project1', //see documentation
'region': 'us-west-2'
#### Example AWS SQS running on Amazon EC2 instance
modules:
enabled: [Queue]
config:
Queue:
'type': 'aws',
'region': 'us-west-2'
## Actions
### addMessageToQueue
Add a message to a queue/tube
```php
<?php
$I->addMessageToQueue('this is a messages', 'default');
?>
```
* `param string` $message Message Body
* `param string` $queue Queue Name
### clearQueue
Clear all messages of the queue/tube
```php
<?php
$I->clearQueue('default');
?>
```
* `param string` $queue Queue Name
### dontSeeEmptyQueue
Check if a queue/tube is NOT empty of all messages
```php
<?php
$I->dontSeeEmptyQueue('default');
?>
```
* `param string` $queue Queue Name
### dontSeeQueueExists
Check if a queue/tube does NOT exist on the queueing server.
```php
<?php
$I->dontSeeQueueExists('default');
?>
```
* `param string` $queue Queue Name
### dontSeeQueueHasCurrentCount
Check if a queue/tube does NOT have a given current number of messages
```php
<?php
$I->dontSeeQueueHasCurrentCount('default', 10);
?>
```
* `param string` $queue Queue Name
* `param int` $expected Number of messages expected
### dontSeeQueueHasTotalCount
Check if a queue/tube does NOT have a given total number of messages
```php
<?php
$I->dontSeeQueueHasTotalCount('default', 10);
?>
```
* `param string` $queue Queue Name
* `param int` $expected Number of messages expected
### grabQueueCurrentCount
Grabber method to get the current number of messages on the queue/tube (pending/ready)
```php
<?php
$I->grabQueueCurrentCount('default');
?>
```
* `param string` $queue Queue Name
* `return` int Count
### grabQueueTotalCount
Grabber method to get the total number of messages on the queue/tube
```php
<?php
$I->grabQueueTotalCount('default');
?>
```
* `param` $queue Queue Name
* `return` int Count
### grabQueues
Grabber method to get the list of queues/tubes on the server
```php
<?php
$queues = $I->grabQueues();
?>
```
* `return` array List of Queues/Tubes
### seeEmptyQueue
Check if a queue/tube is empty of all messages
```php
<?php
$I->seeEmptyQueue('default');
?>
```
* `param string` $queue Queue Name
### seeQueueExists
Check if a queue/tube exists on the queueing server.
```php
<?php
$I->seeQueueExists('default');
?>
```
* `param string` $queue Queue Name
### seeQueueHasCurrentCount
Check if a queue/tube has a given current number of messages
```php
<?php
$I->seeQueueHasCurrentCount('default', 10);
?>
```
* `param string` $queue Queue Name
* `param int` $expected Number of messages expected
### seeQueueHasTotalCount
Check if a queue/tube has a given total number of messages
```php
<?php
$I->seeQueueHasTotalCount('default', 10);
?>
```
* `param string` $queue Queue Name
* `param int` $expected Number of messages expected
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Queue.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,887 @@
# REST
Module for testing REST WebService.
This module can be used either with frameworks or PHPBrowser.
If a framework module is connected, the testing will occur in the application directly.
Otherwise, a PHPBrowser should be specified as a dependency to send requests and receive responses from a server.
## Configuration
* url *optional* - the url of api
This module requires PHPBrowser or any of Framework modules enabled.
### Example
modules:
enabled:
- REST:
depends: PhpBrowser
url: 'http://serviceapp/api/v1/'
## Public Properties
* headers - array of headers going to be sent.
* params - array of sent data
* response - last response (string)
## Parts
* Json - actions for validating Json responses (no Xml responses)
* Xml - actions for validating XML responses (no Json responses)
## Conflicts
Conflicts with SOAP module
## Actions
### amAWSAuthenticated
Allows to send REST request using AWS Authorization
Only works with PhpBrowser
Example
Config -
modules:
enabled:
- REST:
aws:
key: accessKey
secret: accessSecret
service: awsService
region: awsRegion
```php
<?php
$I->amAWSAuthenticated();
?>
```
* `param array` $additionalAWSConfig
@throws ModuleException
### amBearerAuthenticated
Adds Bearer authentication via access token.
* `param` $accessToken
* `[Part]` json
* `[Part]` xml
### amDigestAuthenticated
Adds Digest authentication via username/password.
* `param` $username
* `param` $password
* `[Part]` json
* `[Part]` xml
### amHttpAuthenticated
Adds HTTP authentication via username/password.
* `param` $username
* `param` $password
* `[Part]` json
* `[Part]` xml
### amNTLMAuthenticated
Adds NTLM authentication via username/password.
Requires client to be Guzzle >=6.3.0
Out of scope for functional modules.
Example:
```php
<?php
$I->amNTLMAuthenticated('jon_snow', 'targaryen');
?>
```
* `param` $username
* `param` $password
@throws ModuleException
* `[Part]` json
* `[Part]` xml
### deleteHeader
Deletes the header with the passed name. Subsequent requests
will not have the deleted header in its request.
Example:
```php
<?php
$I->haveHttpHeader('X-Requested-With', 'Codeception');
$I->sendGET('test-headers.php');
// ...
$I->deleteHeader('X-Requested-With');
$I->sendPOST('some-other-page.php');
?>
```
* `param string` $name the name of the header to delete.
* `[Part]` json
* `[Part]` xml
### dontSeeBinaryResponseEquals
Checks if the hash of a binary response is not the same as provided.
```php
<?php
$I->dontSeeBinaryResponseEquals("8c90748342f19b195b9c6b4eff742ded");
?>
```
Opposite to `seeBinaryResponseEquals`
* `param` $hash the hashed data response expected
* `param` $algo the hash algorithm to use. Default md5.
* `[Part]` json
* `[Part]` xml
### dontSeeHttpHeader
Checks over the given HTTP header and (optionally)
its value, asserting that are not there
* `param` $name
* `param` $value
* `[Part]` json
* `[Part]` xml
### dontSeeResponseCodeIs
Checks that response code is not equal to provided value.
```php
<?php
$I->dontSeeResponseCodeIs(200);
// preferred to use \Codeception\Util\HttpCode
$I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK);
```
* `[Part]` json
* `[Part]` xml
* `param` $code
### dontSeeResponseContains
Checks whether last response do not contain text.
* `param` $text
* `[Part]` json
* `[Part]` xml
### dontSeeResponseContainsJson
Opposite to seeResponseContainsJson
* `[Part]` json
* `param array` $json
### dontSeeResponseJsonMatchesJsonPath
Opposite to seeResponseJsonMatchesJsonPath
* `param string` $jsonPath
* `[Part]` json
### dontSeeResponseJsonMatchesXpath
Opposite to seeResponseJsonMatchesXpath
* `param string` $xpath
* `[Part]` json
### dontSeeResponseMatchesJsonType
Opposite to `seeResponseMatchesJsonType`.
* `[Part]` json
@see seeResponseMatchesJsonType
* `param` $jsonType jsonType structure
* `param null` $jsonPath optionally set specific path to structure with JsonPath
* `Available since` 2.1.3
### dontSeeXmlResponseEquals
Checks XML response does not equal to provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
* `param` $xml
* `[Part]` xml
### dontSeeXmlResponseIncludes
Checks XML response does not include provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
* `param` $xml
* `[Part]` xml
### dontSeeXmlResponseMatchesXpath
Checks whether XML response does not match XPath
```php
<?php
$I->dontSeeXmlResponseMatchesXpath('//root/user[@id=1]');
```
* `[Part]` xml
* `param` $xpath
### grabAttributeFromXmlElement
Finds and returns attribute of element.
Element is matched by either CSS or XPath
* `param` $cssOrXPath
* `param` $attribute
* `return` string
* `[Part]` xml
### grabDataFromJsonResponse
Deprecated since 2.0.9 and removed since 2.1.0
* `param` $path
@throws ModuleException
@deprecated
### grabDataFromResponseByJsonPath
Returns data from the current JSON response using [JSONPath](http://goessner.net/articles/JsonPath/) as selector.
JsonPath is XPath equivalent for querying Json structures.
Try your JsonPath expressions [online](http://jsonpath.curiousconcept.com/).
Even for a single value an array is returned.
This method **require [`flow/jsonpath` > 0.2](https://github.com/FlowCommunications/JSONPath/) library to be installed**.
Example:
``` php
<?php
// match the first `user.id` in json
$firstUserId = $I->grabDataFromResponseByJsonPath('$..users[0].id');
$I->sendPUT('/user', array('id' => $firstUserId[0], 'name' => 'davert'));
?>
```
* `param string` $jsonPath
* `return` array Array of matching items
* `Available since` 2.0.9
@throws \Exception
* `[Part]` json
### grabHttpHeader
Returns the value of the specified header name
* `param` $name
* `param Boolean` $first Whether to return the first value or all header values
* `return string|array The first header value if` $first is true, an array of values otherwise
* `[Part]` json
* `[Part]` xml
### grabResponse
Returns current response so that it can be used in next scenario steps.
Example:
``` php
<?php
$user_id = $I->grabResponse();
$I->sendPUT('/user', array('id' => $user_id, 'name' => 'davert'));
?>
```
* `Available since` 1.1
* `return` string
* `[Part]` json
* `[Part]` xml
### grabTextContentFromXmlElement
Finds and returns text contents of element.
Element is matched by either CSS or XPath
* `param` $cssOrXPath
* `return` string
* `[Part]` xml
### haveHttpHeader
Sets HTTP header valid for all next requests. Use `deleteHeader` to unset it
```php
<?php
$I->haveHttpHeader('Content-Type', 'application/json');
// all next requests will contain this header
?>
```
* `param` $name
* `param` $value
* `[Part]` json
* `[Part]` xml
### seeBinaryResponseEquals
Checks if the hash of a binary response is exactly the same as provided.
Parameter can be passed as any hash string supported by hash(), with an
optional second parameter to specify the hash type, which defaults to md5.
Example: Using md5 hash key
```php
<?php
$I->seeBinaryResponseEquals("8c90748342f19b195b9c6b4eff742ded");
?>
```
Example: Using md5 for a file contents
```php
<?php
$fileData = file_get_contents("test_file.jpg");
$I->seeBinaryResponseEquals(md5($fileData));
?>
```
Example: Using sha256 hash
```php
<?php
$fileData = '/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k='; // very small jpeg
$I->seeBinaryResponseEquals(hash("sha256", base64_decode($fileData)), 'sha256');
?>
```
* `param` $hash the hashed data response expected
* `param` $algo the hash algorithm to use. Default md5.
* `[Part]` json
* `[Part]` xml
### seeHttpHeader
Checks over the given HTTP header and (optionally)
its value, asserting that are there
* `param` $name
* `param` $value
* `[Part]` json
* `[Part]` xml
### seeHttpHeaderOnce
Checks that http response header is received only once.
HTTP RFC2616 allows multiple response headers with the same name.
You can check that you didn't accidentally sent the same header twice.
``` php
<?php
$I->seeHttpHeaderOnce('Cache-Control');
?>>
```
* `param` $name
* `[Part]` json
* `[Part]` xml
### seeResponseCodeIs
Checks response code equals to provided value.
```php
<?php
$I->seeResponseCodeIs(200);
// preferred to use \Codeception\Util\HttpCode
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK);
```
* `[Part]` json
* `[Part]` xml
* `param` $code
### seeResponseCodeIsClientError
Checks that the response code is 4xx
### seeResponseCodeIsRedirection
Checks that the response code 3xx
### seeResponseCodeIsServerError
Checks that the response code is 5xx
### seeResponseCodeIsSuccessful
Checks that the response code is 2xx
### seeResponseContains
Checks whether the last response contains text.
* `param` $text
* `[Part]` json
* `[Part]` xml
### seeResponseContainsJson
Checks whether the last JSON response contains provided array.
The response is converted to array with json_decode($response, true)
Thus, JSON is represented by associative array.
This method matches that response array contains provided array.
Examples:
``` php
<?php
// response: {name: john, email: john@gmail.com}
$I->seeResponseContainsJson(array('name' => 'john'));
// response {user: john, profile: { email: john@gmail.com }}
$I->seeResponseContainsJson(array('email' => 'john@gmail.com'));
?>
```
This method recursively checks if one array can be found inside of another.
* `param array` $json
* `[Part]` json
### seeResponseEquals
Checks if response is exactly the same as provided.
* `[Part]` json
* `[Part]` xml
* `param` $response
### seeResponseIsJson
Checks whether last response was valid JSON.
This is done with json_last_error function.
* `[Part]` json
### seeResponseIsXml
Checks whether last response was valid XML.
This is done with libxml_get_last_error function.
* `[Part]` xml
### seeResponseJsonMatchesJsonPath
Checks if json structure in response matches [JsonPath](http://goessner.net/articles/JsonPath/).
JsonPath is XPath equivalent for querying Json structures.
Try your JsonPath expressions [online](http://jsonpath.curiousconcept.com/).
This assertion allows you to check the structure of response json.
This method **require [`flow/jsonpath` > 0.2](https://github.com/FlowCommunications/JSONPath/) library to be installed**.
```json
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
```
```php
<?php
// at least one book in store has author
$I->seeResponseJsonMatchesJsonPath('$.store.book[*].author');
// first book in store has author
$I->seeResponseJsonMatchesJsonPath('$.store.book[0].author');
// at least one item in store has price
$I->seeResponseJsonMatchesJsonPath('$.store..price');
?>
```
* `param string` $jsonPath
* `[Part]` json
* `Available since` 2.0.9
### seeResponseJsonMatchesXpath
Checks if json structure in response matches the xpath provided.
JSON is not supposed to be checked against XPath, yet it can be converted to xml and used with XPath.
This assertion allows you to check the structure of response json.
*
```json
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
```
```php
<?php
// at least one book in store has author
$I->seeResponseJsonMatchesXpath('//store/book/author');
// first book in store has author
$I->seeResponseJsonMatchesXpath('//store/book[1]/author');
// at least one item in store has price
$I->seeResponseJsonMatchesXpath('/store//price');
?>
```
* `param string` $xpath
* `[Part]` json
* `Available since` 2.0.9
### seeResponseMatchesJsonType
Checks that Json matches provided types.
In case you don't know the actual values of JSON data returned you can match them by type.
Starts check with a root element. If JSON data is array it will check the first element of an array.
You can specify the path in the json which should be checked with JsonPath
Basic example:
```php
<?php
// {'user_id': 1, 'name': 'davert', 'is_active': false}
$I->seeResponseMatchesJsonType([
'user_id' => 'integer',
'name' => 'string|null',
'is_active' => 'boolean'
]);
// narrow down matching with JsonPath:
// {"users": [{ "name": "davert"}, {"id": 1}]}
$I->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]');
?>
```
In this case you can match that record contains fields with data types you expected.
The list of possible data types:
* string
* integer
* float
* array (json object is array as well)
* boolean
You can also use nested data type structures:
```php
<?php
// {'user_id': 1, 'name': 'davert', 'company': {'name': 'Codegyre'}}
$I->seeResponseMatchesJsonType([
'user_id' => 'integer|string', // multiple types
'company' => ['name' => 'string']
]);
?>
```
You can also apply filters to check values. Filter can be applied with `:` char after the type declaration.
Here is the list of possible filters:
* `integer:>{val}` - checks that integer is greater than {val} (works with float and string types too).
* `integer:<{val}` - checks that integer is lower than {val} (works with float and string types too).
* `string:url` - checks that value is valid url.
* `string:date` - checks that value is date in JavaScript format: https://weblog.west-wind.com/posts/2014/Jan/06/JavaScript-JSON-Date-Parsing-and-real-Dates
* `string:email` - checks that value is a valid email according to http://emailregex.com/
* `string:regex({val})` - checks that string matches a regex provided with {val}
This is how filters can be used:
```php
<?php
// {'user_id': 1, 'email' => 'davert@codeception.com'}
$I->seeResponseMatchesJsonType([
'user_id' => 'string:>0:<1000', // multiple filters can be used
'email' => 'string:regex(~\@~)' // we just check that @ char is included
]);
// {'user_id': '1'}
$I->seeResponseMatchesJsonType([
'user_id' => 'string:>0', // works with strings as well
}
?>
```
You can also add custom filters y accessing `JsonType::addCustomFilter` method.
See [JsonType reference](http://codeception.com/docs/reference/JsonType).
* `[Part]` json
* `Available since` 2.1.3
* `param array` $jsonType
* `param string` $jsonPath
### seeXmlResponseEquals
Checks XML response equals provided XML.
Comparison is done by canonicalizing both xml`s.
Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
* `param` $xml
* `[Part]` xml
### seeXmlResponseIncludes
Checks XML response includes provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
Example:
``` php
<?php
$I->seeXmlResponseIncludes("<result>1</result>");
?>
```
* `param` $xml
* `[Part]` xml
### seeXmlResponseMatchesXpath
Checks whether XML response matches XPath
```php
<?php
$I->seeXmlResponseMatchesXpath('//root/user[@id=1]');
```
* `[Part]` xml
* `param` $xpath
### sendDELETE
Sends DELETE request to given uri.
* `param` $url
* `param array` $params
* `param array` $files
* `[Part]` json
* `[Part]` xml
### sendGET
Sends a GET request to given uri.
* `param` $url
* `param array` $params
* `[Part]` json
* `[Part]` xml
### sendHEAD
Sends a HEAD request to given uri.
* `param` $url
* `param array` $params
* `[Part]` json
* `[Part]` xml
### sendLINK
Sends LINK request to given uri.
* `param` $url
* `param array` $linkEntries (entry is array with keys "uri" and "link-param")
@link http://tools.ietf.org/html/rfc2068#section-19.6.2.4
@author samva.ua@gmail.com
* `[Part]` json
* `[Part]` xml
### sendOPTIONS
Sends an OPTIONS request to given uri.
* `param` $url
* `param array` $params
* `[Part]` json
* `[Part]` xml
### sendPATCH
Sends PATCH request to given uri.
* `param` $url
* `param array` $params
* `param array` $files
* `[Part]` json
* `[Part]` xml
### sendPOST
Sends a POST request to given uri. Parameters and files can be provided separately.
Example:
```php
<?php
//simple POST call
$I->sendPOST('/message', ['subject' => 'Read this!', 'to' => 'johndoe@example.com']);
//simple upload method
$I->sendPOST('/message/24', ['inline' => 0], ['attachmentFile' => codecept_data_dir('sample_file.pdf')]);
//uploading a file with a custom name and mime-type. This is also useful to simulate upload errors.
$I->sendPOST('/message/24', ['inline' => 0], [
'attachmentFile' => [
'name' => 'document.pdf',
'type' => 'application/pdf',
'error' => UPLOAD_ERR_OK,
'size' => filesize(codecept_data_dir('sample_file.pdf')),
'tmp_name' => codecept_data_dir('sample_file.pdf')
]
]);
```
* `param` $url
* `param array|\JsonSerializable` $params
* `param array` $files A list of filenames or "mocks" of $_FILES (each entry being an array with the following
keys: name, type, error, size, tmp_name (pointing to the real file path). Each key works
as the "name" attribute of a file input field.
@see http://php.net/manual/en/features.file-upload.post-method.php
@see codecept_data_dir()
* `[Part]` json
* `[Part]` xml
### sendPUT
Sends PUT request to given uri.
* `param` $url
* `param array` $params
* `param array` $files
* `[Part]` json
* `[Part]` xml
### sendUNLINK
Sends UNLINK request to given uri.
* `param` $url
* `param array` $linkEntries (entry is array with keys "uri" and "link-param")
@link http://tools.ietf.org/html/rfc2068#section-19.6.2.4
@author samva.ua@gmail.com
* `[Part]` json
* `[Part]` xml
### startFollowingRedirects
Enables automatic redirects to be followed by the client
```php
<?php
$I->startFollowingRedirects();
```
* `[Part]` xml
* `[Part]` json
### stopFollowingRedirects
Prevents automatic redirects to be followed by the client
```php
<?php
$I->stopFollowingRedirects();
```
* `[Part]` xml
* `[Part]` json
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/REST.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,285 @@
# Redis
This module uses the [Predis](https://github.com/nrk/predis) library
to interact with a Redis server.
## Status
* Stability: **beta**
## Configuration
* **`host`** (`string`, default `'127.0.0.1'`) - The Redis host
* **`port`** (`int`, default `6379`) - The Redis port
* **`database`** (`int`, no default) - The Redis database. Needs to be specified.
* **`cleanupBefore`**: (`string`, default `'never'`) - Whether/when to flush the database:
* `suite`: at the beginning of every suite
* `test`: at the beginning of every test
* Any other value: never
### Example (`unit.suite.yml`)
```yaml
modules:
- Redis:
host: '127.0.0.1'
port: 6379
database: 0
cleanupBefore: 'never'
```
## Public Properties
* **driver** - Contains the Predis client/driver
@author Marc Verney <marc@marcverney.net>
## Actions
### cleanup
Delete all the keys in the Redis database
@throws ModuleException
### dontSeeInRedis
Asserts that a key does not exist or, optionally, that it doesn't have the
provided $value
Examples:
``` php
<?php
// With only one argument, only checks the key does not exist
$I->dontSeeInRedis('example:string');
// Checks a String does not exist or its value is not the one provided
$I->dontSeeInRedis('example:string', 'life');
// Checks a List does not exist or its value is not the one provided (order of elements is compared).
$I->dontSeeInRedis('example:list', ['riri', 'fifi', 'loulou']);
// Checks a Set does not exist or its value is not the one provided (order of members is ignored).
$I->dontSeeInRedis('example:set', ['riri', 'fifi', 'loulou']);
// Checks a ZSet does not exist or its value is not the one provided (scores are required, order of members is compared)
$I->dontSeeInRedis('example:zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
// Checks a Hash does not exist or its value is not the one provided (order of members is ignored).
$I->dontSeeInRedis('example:hash', ['riri' => true, 'fifi' => 'Dewey', 'loulou' => 2]);
```
* `param string` $key The key name
* `param mixed` $value Optional. If specified, also checks the key has this
value. Booleans will be converted to 1 and 0 (even inside arrays)
### dontSeeRedisKeyContains
Asserts that a given key does not contain a given item
Examples:
``` php
<?php
// Strings: performs a substring search
$I->dontSeeRedisKeyContains('string', 'bar');
// Lists
$I->dontSeeRedisKeyContains('example:list', 'poney');
// Sets
$I->dontSeeRedisKeyContains('example:set', 'cat');
// ZSets: check whether the zset has this member
$I->dontSeeRedisKeyContains('example:zset', 'jordan');
// ZSets: check whether the zset has this member with this score
$I->dontSeeRedisKeyContains('example:zset', 'jordan', 23);
// Hashes: check whether the hash has this field
$I->dontSeeRedisKeyContains('example:hash', 'magic');
// Hashes: check whether the hash has this field with this value
$I->dontSeeRedisKeyContains('example:hash', 'magic', 32);
```
* `param string` $key The key
* `param mixed` $item The item
* `param null` $itemValue Optional and only used for zsets and hashes. If
specified, the method will also check that the $item has this value/score
* `return` bool
### grabFromRedis
Returns the value of a given key
Examples:
``` php
<?php
// Strings
$I->grabFromRedis('string');
// Lists: get all members
$I->grabFromRedis('example:list');
// Lists: get a specific member
$I->grabFromRedis('example:list', 2);
// Lists: get a range of elements
$I->grabFromRedis('example:list', 2, 4);
// Sets: get all members
$I->grabFromRedis('example:set');
// ZSets: get all members
$I->grabFromRedis('example:zset');
// ZSets: get a range of members
$I->grabFromRedis('example:zset', 3, 12);
// Hashes: get all fields of a key
$I->grabFromRedis('example:hash');
// Hashes: get a specific field of a key
$I->grabFromRedis('example:hash', 'foo');
```
* `param string` $key The key name
@throws ModuleException if the key does not exist
### haveInRedis
Creates or modifies keys
If $key already exists:
- Strings: its value will be overwritten with $value
- Other types: $value items will be appended to its value
Examples:
``` php
<?php
// Strings: $value must be a scalar
$I->haveInRedis('string', 'Obladi Oblada');
// Lists: $value can be a scalar or an array
$I->haveInRedis('list', ['riri', 'fifi', 'loulou']);
// Sets: $value can be a scalar or an array
$I->haveInRedis('set', ['riri', 'fifi', 'loulou']);
// ZSets: $value must be an associative array with scores
$I->haveInRedis('zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
// Hashes: $value must be an associative array
$I->haveInRedis('hash', ['obladi' => 'oblada']);
```
* `param string` $type The type of the key
* `param string` $key The key name
* `param mixed` $value The value
@throws ModuleException
### seeInRedis
Asserts that a key exists, and optionally that it has the provided $value
Examples:
``` php
<?php
// With only one argument, only checks the key exists
$I->seeInRedis('example:string');
// Checks a String exists and has the value "life"
$I->seeInRedis('example:string', 'life');
// Checks the value of a List. Order of elements is compared.
$I->seeInRedis('example:list', ['riri', 'fifi', 'loulou']);
// Checks the value of a Set. Order of members is ignored.
$I->seeInRedis('example:set', ['riri', 'fifi', 'loulou']);
// Checks the value of a ZSet. Scores are required. Order of members is compared.
$I->seeInRedis('example:zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
// Checks the value of a Hash. Order of members is ignored.
$I->seeInRedis('example:hash', ['riri' => true, 'fifi' => 'Dewey', 'loulou' => 2]);
```
* `param string` $key The key name
* `param mixed` $value Optional. If specified, also checks the key has this
value. Booleans will be converted to 1 and 0 (even inside arrays)
### seeRedisKeyContains
Asserts that a given key contains a given item
Examples:
``` php
<?php
// Strings: performs a substring search
$I->seeRedisKeyContains('example:string', 'bar');
// Lists
$I->seeRedisKeyContains('example:list', 'poney');
// Sets
$I->seeRedisKeyContains('example:set', 'cat');
// ZSets: check whether the zset has this member
$I->seeRedisKeyContains('example:zset', 'jordan');
// ZSets: check whether the zset has this member with this score
$I->seeRedisKeyContains('example:zset', 'jordan', 23);
// Hashes: check whether the hash has this field
$I->seeRedisKeyContains('example:hash', 'magic');
// Hashes: check whether the hash has this field with this value
$I->seeRedisKeyContains('example:hash', 'magic', 32);
```
* `param string` $key The key
* `param mixed` $item The item
* `param null` $itemValue Optional and only used for zsets and hashes. If
specified, the method will also check that the $item has this value/score
* `return` bool
### sendCommandToRedis
Sends a command directly to the Redis driver. See documentation at
https://github.com/nrk/predis
Every argument that follows the $command name will be passed to it.
Examples:
``` php
<?php
$I->sendCommandToRedis('incr', 'example:string');
$I->sendCommandToRedis('strLen', 'example:string');
$I->sendCommandToRedis('lPop', 'example:list');
$I->sendCommandToRedis('zRangeByScore', 'example:set', '-inf', '+inf', ['withscores' => true, 'limit' => [1, 2]]);
$I->sendCommandToRedis('flushdb');
```
* `param string` $command The command name
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Redis.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,235 @@
# SOAP
Module for testing SOAP WSDL web services.
Send requests and check if response matches the pattern.
This module can be used either with frameworks or PHPBrowser.
It tries to guess the framework is is attached to.
If a endpoint is a full url then it uses PHPBrowser.
### Using Inside Framework
Please note, that PHP SoapServer::handle method sends additional headers.
This may trigger warning: "Cannot modify header information"
If you use PHP SoapServer with framework, try to block call to this method in testing environment.
## Status
* Maintainer: **davert**
* Stability: **stable**
* Contact: codecept@davert.mail.ua
## Configuration
* endpoint *required* - soap wsdl endpoint
* SOAPAction - replace SOAPAction HTTP header (Set to '' to SOAP 1.2)
## Public Properties
* xmlRequest - last SOAP request (DOMDocument)
* xmlResponse - last SOAP response (DOMDocument)
## Actions
### dontSeeSoapResponseContainsStructure
Opposite to `seeSoapResponseContainsStructure`
* `param` $xml
### dontSeeSoapResponseContainsXPath
Checks XML response doesn't contain XPath locator
``` php
<?php
$I->dontSeeSoapResponseContainsXPath('//root/user[@id=1]');
?>
```
* `param` $xpath
### dontSeeSoapResponseEquals
Checks XML response equals provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
* `param` $xml
### dontSeeSoapResponseIncludes
Checks XML response does not include provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
* `param` $xml
### grabAttributeFrom
Finds and returns attribute of element.
Element is matched by either CSS or XPath
* `Available since` 1.1
* `param` $cssOrXPath
* `param` $attribute
* `return` string
### grabTextContentFrom
Finds and returns text contents of element.
Element is matched by either CSS or XPath
* `Available since` 1.1
* `param` $cssOrXPath
* `return` string
### haveSoapHeader
Prepare SOAP header.
Receives header name and parameters as array.
Example:
``` php
<?php
$I->haveSoapHeader('AuthHeader', array('username' => 'davert', 'password' => '123345'));
```
Will produce header:
```
<soapenv:Header>
<SessionHeader>
<AuthHeader>
<username>davert</username>
<password>12345</password>
</AuthHeader>
</soapenv:Header>
```
* `param` $header
* `param array` $params
### seeResponseCodeIs
@deprecated use seeSoapResponseCodeIs instead
### seeSoapResponseCodeIs
Checks response code from server.
* `param` $code
### seeSoapResponseContainsStructure
Checks XML response contains provided structure.
Response elements will be compared with XML provided.
Only nodeNames are checked to see elements match.
Example:
``` php
<?php
$I->seeSoapResponseContainsStructure("<query><name></name></query>");
?>
```
Use this method to check XML of valid structure is returned.
This method does not use schema for validation.
This method does not require path from root to match the structure.
* `param` $xml
### seeSoapResponseContainsXPath
Checks XML response with XPath locator
``` php
<?php
$I->seeSoapResponseContainsXPath('//root/user[@id=1]');
?>
```
* `param` $xpath
### seeSoapResponseEquals
Checks XML response equals provided XML.
Comparison is done by canonicalizing both xml`s.
Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
Example:
``` php
<?php
$I->seeSoapResponseEquals("<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope><SOAP-ENV:Body><result>1</result></SOAP-ENV:Envelope>");
$dom = new \DOMDocument();
$dom->load($file);
$I->seeSoapRequestIncludes($dom);
```
* `param` $xml
### seeSoapResponseIncludes
Checks XML response includes provided XML.
Comparison is done by canonicalizing both xml`s.
Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
Example:
``` php
<?php
$I->seeSoapResponseIncludes("<result>1</result>");
$I->seeSoapRequestIncludes(\Codeception\Utils\Soap::response()->result->val(1));
$dom = new \DDOMDocument();
$dom->load('template.xml');
$I->seeSoapRequestIncludes($dom);
?>
```
* `param` $xml
### sendSoapRequest
Submits request to endpoint.
Requires of api function name and parameters.
Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
You are allowed to execute as much requests as you need inside test.
Example:
``` php
$I->sendSoapRequest('UpdateUser', '<user><id>1</id><name>notdavert</name></user>');
$I->sendSoapRequest('UpdateUser', \Codeception\Utils\Soap::request()->user
->id->val(1)->parent()
->name->val('notdavert');
```
* `param` $request
* `param` $body
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/SOAP.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,97 @@
# Sequence
Sequence solves data cleanup issue in alternative way.
Instead cleaning up the database between tests,
you can use generated unique names, that should not conflict.
When you create article on a site, for instance, you can assign it a unique name and then check it.
This module has no actions, but introduces a function `sq` for generating unique sequences within test and
`sqs` for generating unique sequences across suite.
### Usage
Function `sq` generates sequence, the only parameter it takes, is id.
You can get back to previously generated sequence using that id:
``` php
<?php
sq('post1'); // post1_521fbc63021eb
sq('post2'); // post2_521fbc6302266
sq('post1'); // post1_521fbc63021eb
```
Example:
``` php
<?php
$I->wantTo('create article');
$I->click('New Article');
$I->fillField('Title', sq('Article'));
$I->fillField('Body', 'Demo article with Lorem Ipsum');
$I->click('save');
$I->see(sq('Article') ,'#articles')
```
Populating Database:
``` php
<?php
for ($i = 0; $i<10; $i++) {
$I->haveInDatabase('users', array('login' => sq("user$i"), 'email' => sq("user$i").'@email.com');
}
?>
```
Cest Suite tests:
``` php
<?php
class UserTest
{
public function createUser(AcceptanceTester $I)
{
$I->createUser(sqs('user') . '@mailserver.com', sqs('login'), sqs('pwd'));
}
public function checkEmail(AcceptanceTester $I)
{
$I->seeInEmailTo(sqs('user') . '@mailserver.com', sqs('login'));
}
public function removeUser(AcceptanceTester $I)
{
$I->removeUser(sqs('user') . '@mailserver.com');
}
}
?>
```
### Config
By default produces unique string with param as a prefix:
```
sq('user') => 'user_876asd8as87a'
```
This behavior can be configured using `prefix` config param.
Old style sequences:
```yaml
Sequence:
prefix: '_'
```
Using id param inside prefix:
```yaml
Sequence:
prefix: '{id}.'
```
## Actions
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/Sequence.php">Help us to improve documentation. Edit module reference</a></div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
# XMLRPC
Module for testing XMLRPC WebService.
This module can be used either with frameworks or PHPBrowser.
It tries to guess the framework is is attached to.
Whether framework is used it operates via standard framework modules.
Otherwise sends raw HTTP requests to url via PHPBrowser.
## Requirements
* Module requires installed php_xmlrpc extension
## Status
* Maintainer: **tiger-seo**
* Stability: **beta**
* Contact: tiger.seo@gmail.com
## Configuration
* url *optional* - the url of api
## Public Properties
* headers - array of headers going to be sent.
* params - array of sent data
* response - last response (string)
@since 1.1.5
@author tiger.seo@gmail.com
## Actions
### haveHttpHeader
Sets HTTP header
* `param string` $name
* `param string` $value
### seeResponseCodeIs
Checks response code.
* `param` $num
### seeResponseIsXMLRPC
Checks weather last response was valid XMLRPC.
This is done with xmlrpc_decode function.
### sendXMLRPCMethodCall
Sends a XMLRPC method call to remote XMLRPC-server.
* `param string` $methodName
* `param array` $parameters
<p>&nbsp;</p><div class="alert alert-warning">Module reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Module/XMLRPC.php">Help us to improve documentation. Edit module reference</a></div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
## Codeception\Util\Autoload
Autoloader, which is fully compatible with PSR-4,
and can be used to autoload your `Helper`, `Page`, and `Step` classes.
#### __construct()
*private* __construct()
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L18)
#### addNamespace()
*public static* addNamespace($prefix, $base_dir, $prepend = null)
Adds a base directory for a namespace prefix.
Example:
```php
<?php
// app\Codeception\UserHelper will be loaded from '/path/to/helpers/UserHelper.php'
Autoload::addNamespace('app\Codeception', '/path/to/helpers');
// LoginPage will be loaded from '/path/to/pageobjects/LoginPage.php'
Autoload::addNamespace('', '/path/to/pageobjects');
Autoload::addNamespace('app\Codeception', '/path/to/controllers');
?>
```
* `param string` $prefix The namespace prefix.
* `param string` $base_dir A base directory for class files in the namespace.
* `param bool` $prepend If true, prepend the base directory to the stack instead of appending it;
this causes it to be searched first rather than last.
* `return` void
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L45)
#### load()
*public static* load($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L88)
#### loadMappedFile()
*protected static* loadMappedFile($prefix, $relative_class)
Load the mapped file for a namespace prefix and relative class.
* `param string` $prefix The namespace prefix.
* `param string` $relative_class The relative class name.
* `return` mixed Boolean false if no mapped file can be loaded, or the name of the mapped file that was loaded.
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L136)
#### register()
*public static* register($namespace, $suffix, $path)
* `deprecated` Use self::addNamespace() instead.
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L75)
#### registerSuffix()
*public static* registerSuffix($suffix, $path)
* `deprecated` Use self::addNamespace() instead.
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L83)
#### requireFile()
*protected static* requireFile($file)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Autoload.php#L156)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/Autoload.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,321 @@
# Console Commands
## DryRun
Shows step by step execution process for scenario driven tests without actually running them.
* `codecept dry-run acceptance`
* `codecept dry-run acceptance MyCest`
* `codecept dry-run acceptance checkout.feature`
* `codecept dry-run tests/acceptance/MyCest.php`
## GenerateSuite
Create new test suite. Requires suite name and actor name
* ``
* `codecept g:suite api` -> api + ApiTester
* `codecept g:suite integration Code` -> integration + CodeTester
* `codecept g:suite frontend Front` -> frontend + FrontTester
## GherkinSnippets
Generates code snippets for matched feature files in a suite.
Code snippets are expected to be implemented in Actor or PageObjects
Usage:
* `codecept gherkin:snippets acceptance` - snippets from all feature of acceptance tests
* `codecept gherkin:snippets acceptance/feature/users` - snippets from `feature/users` dir of acceptance tests
* `codecept gherkin:snippets acceptance user_account.feature` - snippets from a single feature file
* `codecept gherkin:snippets acceptance/feature/users/user_accout.feature` - snippets from feature file in a dir
## Init
## Console
Try to execute test commands in run-time. You may try commands before writing the test.
* `codecept console acceptance` - starts acceptance suite environment. If you use WebDriver you can manipulate browser with Codeception commands.
## ConfigValidate
Validates and prints Codeception config.
Use it do debug Yaml configs
Check config:
* `codecept config`: check global config
* `codecept config unit`: check suite config
Load config:
* `codecept config:validate -c path/to/another/config`: from another dir
* `codecept config:validate -c another_config.yml`: from another config file
Check overriding config values (like in `run` command)
* `codecept config:validate -o "settings: shuffle: true"`: enable shuffle
* `codecept config:validate -o "settings: lint: false"`: disable linting
* `codecept config:validate -o "reporters: report: \Custom\Reporter" --report`: use custom reporter
## GenerateGroup
Creates empty GroupObject - extension which handles all group events.
* `codecept g:group Admin`
## GenerateCept
Generates Cept (scenario-driven test) file:
* `codecept generate:cept suite Login`
* `codecept g:cept suite subdir/subdir/testnameCept.php`
* `codecept g:cept suite LoginCept -c path/to/project`
## Run
Executes tests.
Usage:
* `codecept run acceptance`: run all acceptance tests
* `codecept run tests/acceptance/MyCept.php`: run only MyCept
* `codecept run acceptance MyCept`: same as above
* `codecept run acceptance MyCest:myTestInIt`: run one test from a Cest
* `codecept run acceptance checkout.feature`: run feature-file
* `codecept run acceptance -g slow`: run tests from *slow* group
* `codecept run unit,functional`: run only unit and functional suites
Verbosity modes:
* `codecept run -v`:
* `codecept run --steps`: print step-by-step execution
* `codecept run -vv`:
* `codecept run --debug`: print steps and debug information
* `codecept run -vvv`: print internal debug information
Load config:
* `codecept run -c path/to/another/config`: from another dir
* `codecept run -c another_config.yml`: from another config file
Override config values:
* `codecept run -o "settings: shuffle: true"`: enable shuffle
* `codecept run -o "settings: lint: false"`: disable linting
* `codecept run -o "reporters: report: \Custom\Reporter" --report`: use custom reporter
Run with specific extension
* `codecept run --ext Recorder` run with Recorder extension enabled
* `codecept run --ext DotReporter` run with DotReporter printer
* `codecept run --ext "My\Custom\Extension"` run with an extension loaded by class name
Full reference:
```
Arguments:
suite suite to be tested
test test to be run
Options:
-o, --override=OVERRIDE Override config values (multiple values allowed)
--config (-c) Use custom path for config
--report Show output in compact style
--html Generate html with results (default: "report.html")
--xml Generate JUnit XML Log (default: "report.xml")
--tap Generate Tap Log (default: "report.tap.log")
--json Generate Json Log (default: "report.json")
--colors Use colors in output
--no-colors Force no colors in output (useful to override config file)
--silent Only outputs suite names and final results
--steps Show steps in output
--debug (-d) Show debug and scenario output
--coverage Run with code coverage (default: "coverage.serialized")
--coverage-html Generate CodeCoverage HTML report in path (default: "coverage")
--coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml")
--coverage-text Generate CodeCoverage text report in file (default: "coverage.txt")
--coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit")
--no-exit Don't finish with exit code
--group (-g) Groups of tests to be executed (multiple values allowed)
--skip (-s) Skip selected suites (multiple values allowed)
--skip-group (-x) Skip selected groups (multiple values allowed)
--env Run tests in selected environments. (multiple values allowed, environments can be merged with ',')
--fail-fast (-f) Stop after first failure
--help (-h) Display this help message.
--quiet (-q) Do not output any message.
--verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version (-V) Display this application version.
--ansi Force ANSI output.
--no-ansi Disable ANSI output.
--no-interaction (-n) Do not ask any interactive question.
```
## SelfUpdate
Auto-updates phar archive from official site: 'http://codeception.com/codecept.phar' .
* `php codecept.phar self-update`
@author Franck Cassedanne <franck@cassedanne.com>
## CompletionFallback
## GenerateTest
Generates skeleton for Unit Test that extends `Codeception\TestCase\Test`.
* `codecept g:test unit User`
* `codecept g:test unit "App\User"`
## Build
Generates Actor classes (initially Guy classes) from suite configs.
Starting from Codeception 2.0 actor classes are auto-generated. Use this command to generate them manually.
* `codecept build`
* `codecept build path/to/project`
## GenerateHelper
Creates empty Helper class.
* `codecept g:helper MyHelper`
* `codecept g:helper "My\Helper"`
## Bootstrap
Creates default config, tests directory and sample suites for current project.
Use this command to start building a test suite.
By default it will create 3 suites **acceptance**, **functional**, and **unit**.
* `codecept bootstrap` - creates `tests` dir and `codeception.yml` in current dir.
* `codecept bootstrap --empty` - creates `tests` dir without suites
* `codecept bootstrap --namespace Frontend` - creates tests, and use `Frontend` namespace for actor classes and helpers.
* `codecept bootstrap --actor Wizard` - sets actor as Wizard, to have `TestWizard` actor in tests.
* `codecept bootstrap path/to/the/project` - provide different path to a project, where tests should be placed
## GenerateEnvironment
Generates empty environment configuration file into envs dir:
* `codecept g:env firefox`
Required to have `envs` path to be specified in `codeception.yml`
## GenerateFeature
Generates Feature file (in Gherkin):
* `codecept generate:feature suite Login`
* `codecept g:feature suite subdir/subdir/login.feature`
* `codecept g:feature suite login.feature -c path/to/project`
## GenerateScenarios
Generates user-friendly text scenarios from scenario-driven tests (Cest, Cept).
* `codecept g:scenarios acceptance` - for all acceptance tests
* `codecept g:scenarios acceptance --format html` - in html format
* `codecept g:scenarios acceptance --path doc` - generate scenarios to `doc` dir
## GenerateStepObject
Generates StepObject class. You will be asked for steps you want to implement.
* `codecept g:stepobject acceptance AdminSteps`
* `codecept g:stepobject acceptance UserSteps --silent` - skip action questions
## Clean
Recursively cleans `output` directory and generated code.
* `codecept clean`
## GherkinSteps
Prints all steps from all Gherkin contexts for a specific suite
```
codecept gherkin:steps acceptance
```
## GenerateCest
Generates Cest (scenario-driven object-oriented test) file:
* `codecept generate:cest suite Login`
* `codecept g:cest suite subdir/subdir/testnameCest.php`
* `codecept g:cest suite LoginCest -c path/to/project`
* `codecept g:cest "App\Login"`
## GeneratePageObject
Generates PageObject. Can be generated either globally, or just for one suite.
If PageObject is generated globally it will act as UIMap, without any logic in it.
* `codecept g:page Login`
* `codecept g:page Registration`
* `codecept g:page acceptance Login`

View File

@@ -0,0 +1,159 @@
# Configuration
## Global Configuration
Configuration file `codeception.yml` is generated by `codecept bootstrap` command. It has preconfigured settings you can change.
Here are global options you can change inside configuration:
* `actor: Tester`: changes suffix for Actor classes. This defines a rule to generate new test suites. If you change `Tester` to `Ninja`, and generate new `api` test suite, you will get `ApiNinja` actor class.
* `namespace`: set a namespace for tests. All new tests and support classes will be generated under that namespace. Allows to configure [multiple test setups for one runner](http://codeception.com/docs/08-Customization#Namespaces).
* `include: []`: include additional Codeception configurations for [multiple applications setup](http://codeception.com/docs/08-Customization#Namespaces).
* `paths` directories used by Codeception. Default values are:
```yaml
paths:
# where the tests stored
tests: tests
# directory for fixture data
data: tests/_data
# directory for support code
support: tests/_support
# directory for output
output: tests/_output
# directory for environment configuration
envs: tests/_envs
```
* `settings`: provide additional options for test runner. They may dramatically change the way Codeception is executed. For instance, take a note of `shuffle` option which allows to randomize tests execution order and `lint` option that toggles parsing a test file (using `php -l`) before loading it.
```yaml
settings:
# name of bootstrap that will be used
# each bootstrap file should be
# inside a suite directory.
bootstrap: _bootstrap.php
# enable/disable syntax of test files before loading
# for php < 7 exec('php -l') is used
# disable if you need to speed up tests execution
lint: true
# randomize test order
shuffle: true
# by default it's false on Windows
# use [ANSICON](https://github.com/adoxa/ansicon) to colorize output.
colors: true
# Generate XML JUnit report using strict schema
# Avoid putting additional report fields like steps or scenario names tot it
# Required for XML reports on Jenkins CI
strict_xml: false
# Tests (especially functional) can take a lot of memory
# We set a high limit for them by default.
memory_limit: 1024M
# This value controls whether PHPUnit attempts to backup global variables
# See https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.backupGlobals
backup_globals: true
# PHPUnit can be strict about tests that do not test anything
# See https://phpunit.de/manual/current/en/risky-tests.html#risky-tests.useless-tests
report_useless_tests: false
# PHPUnit can be strict about output during tests.
# See https://phpunit.de/manual/current/en/risky-tests.html#risky-tests.output-during-test-execution
disallow_test_output: false
# PHPUnit can be strict about tests that manipulate global state.
# See https://phpunit.de/manual/current/en/risky-tests.html#risky-tests.global-state-manipulation
be_strict_about_changes_to_global_state: false
# Log the incomplete and skipped tests into junit report
# See https://phpunit.de/manual/current/en/appendixes.configuration.html
# Section logging > junit
log_incomplete_skipped: false
```
* `modules`: allows to create shared module configuration for all included suites.
```yaml
modules:
config:
Db:
dsn: ''
user: ''
password: ''
dump: tests/_data/dump.sql
```
* `extends`: allows you to specify a file (relative to the `codeception.yml` file) that holds some already pre-defined values. This can be used to always use the same configuration for modules or whatever.
* `extensions`: allows to enable and configure [Codeception extensions](http://codeception.com/docs/08-Customization#Extension), [Group Objects](http://codeception.com/docs/08-Customization#Group-Objects), and [Custom Commands](http://codeception.com/docs/08-Customization#Custom-Commands).
* `reporters`: allows to [change default reporters](http://codeception.com/docs/08-Customization#Custom-Reporters) of Codeception
* `coverage`: [CodeCoverage](http://codeception.com/docs/11-Codecoverage#Configuration) settings.
* `params`: allows to pass [external parameters](http://codeception.com/docs/06-ModulesAndHelpers#Dynamic-Configuration-With-Params) into module configuration.
* `gherkin`: BDD-specific [Gherkin options](http://codeception.com/docs/07-BDD#Configuration).
## Suite Configuration
Each generated suite have its own configuration inside directory set by `paths: tests: ` configuration option in `codeception.yml`. Each suite configuration is named like `suitename.suite.yml`. It allows to enable and configure modules, and more.
* `actor`: name of the actor class for current suite.
* `modules`: list of enabled modules with their configuration.
```yaml
modules:
# enabled modules and helpers
enabled:
# built-in modules are listed by their names
- PhpBrowser:
# module configuration
url: http://localhost
# this module is pre-configured in global config
- Db
# helper names are listed by their class names
# by convention their names start with \
- \Helper\Acceptance
# additional modules configuration
# can be used for modules which are not currently enabled
config:
WebDriver:
browser: firefox
# list of modules disabled for this suite
disabled:
- WebDriver
```
* `extends`: allows you to specify a file (relative to the `*.suite.yml` file) that holds some already pre-defined values. This can be used to always use the same configuration for modules or whatever.
* `namespace`: default namespace of actor, support classes and tests.
* `suite_namespace`: default namespace for new tests of this suite (ignores `namespace` option)
* `env`: override any configuration per [environment](http://codeception.com/docs/07-AdvancedUsage#Environments).
* `groups`: [groups](http://codeception.com/docs/07-AdvancedUsage#Groups) with the list of tests of for corresponding group.
* `formats`: [formats](http://codeception.com/docs/07-AdvancedUsage#Formats) with the list of extra test format classes.
* `coverage`: pre suite [CodeCoverage](http://codeception.com/docs/11-Codecoverage#Configuration) settings.
* `gherkin`: per suite [BDD Gherkin](http://codeception.com/docs/07-BDD#Configuration) settings.
* `error_level`: [error level](http://codeception.com/docs/04-FunctionalTests#Error-Reporting) for runner in current suite. Should be specified for unit, integration, functional tests. Passes value to `error_reporting` function.
## Config Templates (dist)
To provide the same configuration template for your development team, you can create a `codeception.dist.yml` config file, which will be loaded before `codeception.yml`. The dist config provides shared options, while local `codeception.yml` files override them on a per-installation basis. Therefore, `codeception.yml` should be ignored by your VCS system.
Config templates can also be used for suite configuration, by creating a `suitename.suite.dist.yml` file.
Configuration loading order:
1. `codeception.dist.yml`
2. `codeception.yml`
3. `acceptance.suite.dist.yml`
4. `acceptance.suite.yml`
5. environment config

View File

@@ -0,0 +1,43 @@
## Codeception\Util\Fixtures
Really basic class to store data in global array and use it in Cests/Tests.
```php
<?php
Fixtures::add('user1', ['name' => 'davert']);
Fixtures::get('user1');
Fixtures::exists('user1');
?>
```
#### add()
*public static* add($name, $data)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Fixtures.php#L21)
#### cleanup()
*public static* cleanup()
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Fixtures.php#L35)
#### exists()
*public static* exists($name)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Fixtures.php#L40)
#### get()
*public static* get($name)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Fixtures.php#L26)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/Fixtures.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,19 @@
# Shorthand Functions
Shorthand functions can be used in your Codeception tests or helpers.
#### codecept_debug($data)
Prints information when running in debug mode. String, array, or object can be provided as argument.
#### codecept_output_dir()
Returns absolute path to output directory (`tests/_output`)
#### codecept_root_dir()
Returns absolute path to the root directory (where `codeception.yml` is located)
#### codecept_data_dir()
Returns absolute path to data directory (`tests/_data`)

View File

@@ -0,0 +1,40 @@
## Codeception\Util\HttpCode
Class containing constants of HTTP Status Codes
and method to print HTTP code with its description.
Usage:
```php
<?php
use \Codeception\Util\HttpCode;
// using REST, PhpBrowser, or any Framework module
$I->seeResponseCodeIs(HttpCode::OK);
$I->dontSeeResponseCodeIs(HttpCode::NOT_FOUND);
```
#### getDescription()
*public static* getDescription($code)
Returns string with HTTP code and its description
```php
<?php
HttpCode::getDescription(200); // '200 (OK)'
HttpCode::getDescription(401); // '401 (Unauthorized)'
```
* `param` $code
* `return` mixed
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/HttpCode.php#L155)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/HttpCode.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,219 @@
## Codeception\InitTemplate
* *Uses* `Codeception\Command\Shared\FileSystem`, `Codeception\Command\Shared\Style`
Codeception templates allow creating a customized setup and configuration for your project.
An abstract class for installation template. Each init template should extend it and implement a `setup` method.
Use it to build a custom setup class which can be started with `codecept init` command.
```php
<?php
namespace Codeception\Template; // it is important to use this namespace so codecept init could locate this template
class CustomInstall extends \Codeception\InitTemplate
{
public function setup()
{
// implement this
}
}
```
This class provides various helper methods for building customized setup
#### __construct()
*public* __construct($input, $output)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L65)
#### addStyles()
*public* addStyles($output)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Command/Shared/Style.php#L9)
#### ask()
*protected* ask($question, $answer = null)
```php
<?php
// propose firefox as default browser
$this->ask('select the browser of your choice', 'firefox');
// propose firefox or chrome possible options
$this->ask('select the browser of your choice', ['firefox', 'chrome']);
// ask true/false question
$this->ask('do you want to proceed (y/n)', true);
```
* `param` $question
* `param null` $answer
* `return` mixed|string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L107)
#### breakParts()
*protected* breakParts($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Namespaces.php#L6)
#### checkInstalled()
*protected* checkInstalled($dir = null)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L208)
#### completeSuffix()
*protected* completeSuffix($filename, $suffix)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Command/Shared/FileSystem.php#L25)
#### createActor()
*protected* createActor($name, $directory, $suiteConfig)
Create an Actor class and generate actions for it.
Requires a suite config as array in 3rd parameter.
* `param` $name
* `param` $directory
* `param` $suiteConfig
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L223)
#### createDirectoryFor()
*protected* createDirectoryFor($basePath, $className = null)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Command/Shared/FileSystem.php#L10)
#### createEmptyDirectory()
*protected* createEmptyDirectory($dir)
Create an empty directory and add a placeholder file into it
* `param` $dir
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L195)
#### createFile()
*protected* createFile($filename, $contents, $force = null, $flags = null)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Command/Shared/FileSystem.php#L46)
#### createHelper()
*protected* createHelper($name, $directory)
Create a helper class inside a directory
* `param` $name
* `param` $directory
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L174)
#### getNamespaceHeader()
*protected* getNamespaceHeader($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Namespaces.php#L31)
#### getNamespaceString()
*protected* getNamespaceString($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Namespaces.php#L25)
#### getNamespaces()
*protected* getNamespaces($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Namespaces.php#L40)
#### getShortClassName()
*protected* getShortClassName($class)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Namespaces.php#L19)
#### gitIgnore()
*protected* gitIgnore($path)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L201)
#### initDir()
*public* initDir($workDir)
Change the directory where Codeception should be installed.
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L75)
#### removeSuffix()
*protected* removeSuffix($classname, $suffix)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Command/Shared/FileSystem.php#L40)
#### say()
*protected* say($message = null)
Print a message to console.
```php
<?php
$this->say('Welcome to Setup');
```
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L136)
#### sayInfo()
*protected* sayInfo($message)
Print info message
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L163)
#### saySuccess()
*protected* saySuccess($message)
Print a successful message
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L145)
#### sayWarning()
*protected* sayWarning($message)
Print warning message
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L154)
#### setup()
*abstract public* setup()
Override this class to create customized setup.
* `return` mixed
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php#L88)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/InitTemplate.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,107 @@
## Codeception\Util\JsonType
JsonType matches JSON structures against templates.
You can specify the type of fields in JSON or add additional validation rules.
JsonType is used by REST module in `seeResponseMatchesJsonType` and `dontSeeResponseMatchesJsonType` methods.
Usage example:
```php
<?php
$jsonType = new JsonType(['name' => 'davert', 'id' => 1]);
$jsonType->matches([
'name' => 'string:!empty',
'id' => 'integer:>0|string:>0',
]); // => true
$jsonType->matches([
'id' => 'string',
]); // => `id: 1` is not of type string
?>
```
Class JsonType
@package Codeception\Util
#### __construct()
*public* __construct($jsonArray)
Creates instance of JsonType
Pass an array or `\Codeception\Util\JsonArray` with data.
If non-associative array is passed - the very first element of it will be used for matching.
* `param` $jsonArray array|\Codeception\Util\JsonArray
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L42)
#### addCustomFilter()
*public static* addCustomFilter($name, callable $callable)
Adds custom filter to JsonType list.
You should specify a name and parameters of a filter.
Example:
```php
<?php
JsonType::addCustomFilter('slug', function($value) {
return strpos(' ', $value) !== false;
});
// => use it as 'string:slug'
// add custom function to matcher with `len($val)` syntax
// parameter matching patterns should be valid regex and start with `/` char
JsonType::addCustomFilter('/len\((.*?)\)/', function($value, $len) {
return strlen($value) == $len;
});
// use it as 'string:len(5)'
?>
```
* `param` $name
* `param callable` $callable
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L76)
#### cleanCustomFilters()
*public static* cleanCustomFilters()
Removes all custom filters
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L84)
#### matchFilter()
*protected* matchFilter($filter, $value)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L158)
#### matches()
*public* matches(array $jsonType)
Checks data against passed JsonType.
If matching fails function returns a string with a message describing failure.
On success returns `true`.
* `param array` $jsonType
* `return` bool|string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L97)
#### typeComparison()
*protected* typeComparison($data, $jsonType)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/JsonType.php#L116)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/JsonType.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,308 @@
## Codeception\Util\Locator
Set of useful functions for using CSS and XPath locators.
Please check them before writing complex functional or acceptance tests.
#### combine()
*public static* combine($selector1, $selector2)
Applies OR operator to any number of CSS or XPath selectors.
You can mix up CSS and XPath selectors here.
```php
<?php
use \Codeception\Util\Locator;
$I->see('Title', Locator::combine('h1','h2','h3'));
?>
```
This will search for `Title` text in either `h1`, `h2`, or `h3` tag.
You can also combine CSS selector with XPath locator:
```php
<?php
use \Codeception\Util\Locator;
$I->fillField(Locator::combine('form input[type=text]','//form/textarea[2]'), 'qwerty');
?>
```
As a result the Locator will produce a mixed XPath value that will be used in fillField action.
* `static`
* `param` $selector1
* `param` $selector2
* `throws` \Exception
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L50)
#### contains()
*public static* contains($element, $text)
Locates an element containing a text inside.
Either CSS or XPath locator can be passed, however they will be converted to XPath.
```php
<?php
use Codeception\Util\Locator;
Locator::contains('label', 'Name'); // label containing name
Locator::contains('div[@contenteditable=true]', 'hello world');
```
* `param` $element
* `param` $text
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L292)
#### elementAt()
*public static* elementAt($element, $position)
Locates element at position.
Either CSS or XPath locator can be passed as locator,
position is an integer. If a negative value is provided, counting starts from the last element.
First element has index 1
```php
<?php
use Codeception\Util\Locator;
Locator::elementAt('//table/tr', 2); // second row
Locator::elementAt('//table/tr', -1); // last row
Locator::elementAt('table#grind>tr', -2); // previous than last row
```
* `param string` $element CSS or XPath locator
* `param int` $position xpath index
* `return` mixed
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L318)
#### find()
*public static* find($element, array $attributes)
Finds element by it's attribute(s)
```php
<?php
use \Codeception\Util\Locator;
$I->seeElement(Locator::find('img', ['title' => 'diagram']));
```
* `static`
* `param` $element
* `param` $attributes
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L159)
#### firstElement()
*public static* firstElement($element)
Locates first element of group elements.
Either CSS or XPath locator can be passed as locator,
Equal to `Locator::elementAt($locator, 1)`
```php
<?php
use Codeception\Util\Locator;
Locator::firstElement('//table/tr');
```
* `param` $element
* `return` mixed
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L348)
#### href()
*public static* href($url)
Matches the *a* element with given URL
```php
<?php
use \Codeception\Util\Locator;
$I->see('Log In', Locator::href('/login.php'));
?>
```
* `static`
* `param` $url
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L79)
#### humanReadableString()
*public static* humanReadableString($selector)
Transforms strict locator, \Facebook\WebDriver\WebDriverBy into a string represenation
* `param` $selector
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L381)
#### isCSS()
*public static* isCSS($selector)
Checks that provided string is CSS selector
```php
<?php
Locator::isCSS('#user .hello') => true
Locator::isCSS('body') => true
Locator::isCSS('//body/p/user') => false
```
* `param` $selector
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L186)
#### isClass()
*public static* isClass($class)
Checks that a string is valid CSS class
```php
<?php
Locator::isClass('.hello') => true
Locator::isClass('body') => false
Locator::isClass('//body/p/user') => false
```
* `param` $class
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L270)
#### isID()
*public static* isID($id)
Checks that a string is valid CSS ID
```php
<?php
Locator::isID('#user') => true
Locator::isID('body') => false
Locator::isID('//body/p/user') => false
```
* `param` $id
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L252)
#### isPrecise()
*public static* isPrecise($locator)
* `param` $locator
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L221)
#### isXPath()
*public static* isXPath($locator)
Checks that locator is an XPath
```php
<?php
Locator::isXPath('#user .hello') => false
Locator::isXPath('body') => false
Locator::isXPath('//body/p/user') => true
```
* `param` $locator
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L210)
#### lastElement()
*public static* lastElement($element)
Locates last element of group elements.
Either CSS or XPath locator can be passed as locator,
Equal to `Locator::elementAt($locator, -1)`
```php
<?php
use Codeception\Util\Locator;
Locator::lastElement('//table/tr');
```
* `param` $element
* `return` mixed
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L369)
#### option()
*public static* option($value)
Matches option by text:
```php
<?php
use Codeception\Util\Locator;
$I->seeElement(Locator::option('Male'), '#select-gender');
```
* `param` $value
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L124)
#### tabIndex()
*public static* tabIndex($index)
Matches the element with given tab index
Do you often use the `TAB` key to navigate through the web page? How do your site respond to this navigation?
You could try to match elements by their tab position using `tabIndex` method of `Locator` class.
```php
<?php
use \Codeception\Util\Locator;
$I->fillField(Locator::tabIndex(1), 'davert');
$I->fillField(Locator::tabIndex(2) , 'qwerty');
$I->click('Login');
?>
```
* `static`
* `param` $index
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L105)
#### toXPath()
*protected static* toXPath($selector)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Locator.php#L129)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/Locator.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,364 @@
# Mocks
Declare mocks inside `Codeception\Test\Unit` class.
If you want to use mocks outside it, check the reference for [Codeception/Stub](https://github.com/Codeception/Stub) library.
#### *public* make($class, $params = null)
Instantiates a class without executing a constructor.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
$this->make('User');
$this->make('User', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->make(new User, ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->make('User', ['save' => function () { return true; }]);
$this->make('User', ['save' => true]);
```
* `param mixed` $class - A class to be mocked
* `param array` $params - properties and methods to set
@return object - mock
@throws \RuntimeException when class does not exist
@throws \Exception
#### *public* makeEmpty($class, $params = null)
Instantiates class having all methods replaced with dummies.
Constructor is not triggered.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
$this->makeEmpty('User');
$this->makeEmpty('User', ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
$this->makeEmpty(new User, ['name' => 'davert']);
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->makeEmpty('User', ['save' => function () { return true; }]);
$this->makeEmpty('User', ['save' => true));
```
* `param mixed` $class
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
@return object
@throws \Exception
#### *public* makeEmptyExcept($class, $method, $params = null)
Instantiates class having all methods replaced with dummies except one.
Constructor is not triggered.
Properties and methods can be replaced.
Even protected and private properties can be set.
``` php
<?php
$this->makeEmptyExcept('User', 'save');
$this->makeEmptyExcept('User', 'save', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
* $this->makeEmptyExcept(new User, 'save');
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
$this->makeEmptyExcept('User', 'save', ['isValid' => true]);
```
* `param mixed` $class
* `param string` $method
* `param array` $params
@return object
@throws \Exception
#### *public* construct($class, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->construct('User', ['autosave' => false]);
$this->construct('User', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->construct(new User, ['autosave' => false), ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->construct('User', [], ['save' => function () { return true; }]);
$this->construct('User', [], ['save' => true]);
?>
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
@return object
@throws \Exception
#### *public* constructEmpty($class, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor with all methods replaced with dummies.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->constructEmpty('User', ['autosave' => false]);
$this->constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
$this->constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->constructEmpty('User', array(), array('save' => function () { return true; }));
$this->constructEmpty('User', array(), array('save' => true));
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
$this->constructEmpty('User', [], [
'save' => \Codeception\Stub\Expected::once()
]);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
@return object
#### *public* constructEmptyExcept($class, $method, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->constructEmptyExcept('User', 'save');
$this->constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
$this->constructEmptyExcept('User', 'save', [], ['save' => true]);
?>
```
* `param mixed` $class
* `param string` $method
* `param array` $constructorParams
* `param array` $params
@return object
#### *public static* never($params = null)
Checks if a method never has been invoked
If method invoked, it will immediately throw an
exception.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make('User', [
'getName' => Expected::never(),
'someMethod' => function() {}
]);
$user->someMethod();
?>
```
* `param mixed` $params
@return StubMarshaler
#### *public static* once($params = null)
Checks if a method has been invoked exactly one
time.
If the number is less or greater it will later be checked in verify() and also throw an
exception.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::once('Davert'),
'someMethod' => function() {}
)
);
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::once(function() { return Faker::name(); });
```
* `param mixed` $params
@return StubMarshaler
#### *public static* atLeastOnce($params = null)
Checks if a method has been invoked at least one
time.
If the number of invocations is 0 it will throw an exception in verify.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::atLeastOnce('Davert')),
'someMethod' => function() {}
)
);
$user->getName();
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::atLeastOnce(function() { return Faker::name(); });
```
* `param mixed` $params
@return StubMarshaler
#### *public static* exactly($count, $params = null)
Checks if a method has been invoked a certain amount
of times.
If the number of invocations exceeds the value it will immediately throw an
exception,
If the number is less it will later be checked in verify() and also throw an
exception.
``` php
<?php
use \Codeception\Stub;
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::exactly(3, 'Davert'),
'someMethod' => function() {}
)
);
$user->getName();
$user->getName();
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::exactly(function() { return Faker::name() });
```
* `param int` $count
* `param mixed` $params
@return StubMarshaler

View File

@@ -0,0 +1,741 @@
## Codeception\Module
* *Uses* `Codeception\Util\Shared\Asserts`
Basic class for Modules and Helpers.
You must extend from it while implementing own helpers.
Public methods of this class start with `_` prefix in order to ignore them in actor classes.
Module contains **HOOKS** which allow to handle test execution routine.
#### $includeInheritedActions
*public static* **$includeInheritedActions**
By setting it to false module wan't inherit methods of parent class.
type `bool`
#### $onlyActions
*public static* **$onlyActions**
Allows to explicitly set what methods have this class.
type `array`
#### $excludeActions
*public static* **$excludeActions**
Allows to explicitly exclude actions from module.
type `array`
#### $aliases
*public static* **$aliases**
Allows to rename actions
type `array`
#### __construct()
*public* __construct($moduleContainer, $config = null)
Module constructor.
Requires module container (to provide access between modules of suite) and config.
* `param ModuleContainer` $moduleContainer
* `param null` $config
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L70)
#### _after()
*public* _after($test)
**HOOK** executed after test
* `param TestInterface` $test
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L253)
#### _afterStep()
*public* _afterStep($step)
**HOOK** executed after step
* `param Step` $step
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L235)
#### _afterSuite()
*public* _afterSuite()
**HOOK** executed after suite
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L217)
#### _before()
*public* _before($test)
**HOOK** executed before test
* `param TestInterface` $test
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L244)
#### _beforeStep()
*public* _beforeStep($step)
**HOOK** executed before step
* `param Step` $step
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L226)
#### _beforeSuite()
*public* _beforeSuite($settings = null)
**HOOK** executed before suite
* `param array` $settings
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L210)
#### _failed()
*public* _failed($test, $fail)
**HOOK** executed when test fails but before `_after`
* `param TestInterface` $test
* `param \Exception` $fail
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L263)
#### _getConfig()
*public* _getConfig($key = null)
Get config values or specific config item.
* `param null` $key
* `return` array|mixed|null
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L338)
#### _getName()
*public* _getName()
Returns a module name for a Module, a class name for Helper
* `return` string
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L177)
#### _hasRequiredFields()
*public* _hasRequiredFields()
Checks if a module has required fields
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L193)
#### _initialize()
*public* _initialize()
**HOOK** triggered after module is created and configuration is loaded
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L201)
#### _reconfigure()
*public* _reconfigure($config)
Allows to redefine config for a specific test.
Config is restored at the end of a test.
```php
<?php
// cleanup DB only for specific group of tests
public function _before(Test $test) {
if (in_array('cleanup', $test->getMetadata()->getGroups()) {
$this->getModule('Db')->_reconfigure(['cleanup' => true]);
}
}
```
* `param` $config
* `throws` Exception\ModuleConfigException
* `throws` ModuleException
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L119)
#### _resetConfig()
*public* _resetConfig()
Reverts config changed by `_reconfigure`
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L137)
#### _setConfig()
*public* _setConfig($config)
Allows to define initial module config.
Can be used in `_beforeSuite` hook of Helpers or Extensions
```php
<?php
public function _beforeSuite($settings = []) {
$this->getModule('otherModule')->_setConfig($this->myOtherConfig);
}
```
* `param` $config
* `throws` Exception\ModuleConfigException
* `throws` ModuleException
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L95)
#### assert()
*protected* assert($arguments, $not = null)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L6)
#### assertArrayHasKey()
*protected* assertArrayHasKey($key, $actual, $description = null)
* `param` $key
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L385)
#### assertArrayNotHasKey()
*protected* assertArrayNotHasKey($key, $actual, $description = null)
* `param` $key
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L395)
#### assertArraySubset()
*protected* assertArraySubset($subset, $array, $strict = null, $message = null)
Checks that array contains subset.
* `param array` $subset
* `param array` $array
* `param bool` $strict
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L408)
#### assertContains()
*protected* assertContains($needle, $haystack, $message = null)
Checks that haystack contains needle
* `param` $needle
* `param` $haystack
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L149)
#### assertCount()
*protected* assertCount($expectedCount, $actual, $description = null)
* `param` $expectedCount
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L418)
#### assertEmpty()
*protected* assertEmpty($actual, $message = null)
Checks that variable is empty.
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L221)
#### assertEquals()
*protected* assertEquals($expected, $actual, $message = null, $delta = null)
Checks that two variables are equal.
* `param` $expected
* `param` $actual
* `param string` $message
* `param float` $delta
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L35)
#### assertFalse()
*protected* assertFalse($condition, $message = null)
Checks that condition is negative.
* `param` $condition
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L287)
#### assertFileExists()
*protected* assertFileExists($filename, $message = null)
Checks if file exists
* `param string` $filename
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L334)
#### assertFileNotExists()
*protected* assertFileNotExists($filename, $message = null)
Checks if file doesn't exist
* `param string` $filename
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L346)
#### assertGreaterOrEquals()
*protected* assertGreaterOrEquals($expected, $actual, $description = null)
* `param` $expected
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L356)
#### assertGreaterThan()
*protected* assertGreaterThan($expected, $actual, $message = null)
Checks that actual is greater than expected
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L84)
#### assertGreaterThanOrEqual()
*protected* assertGreaterThanOrEqual($expected, $actual, $message = null)
Checks that actual is greater or equal than expected
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L104)
#### assertGreaterThen()
*protected* assertGreaterThen($expected, $actual, $message = null)
* `deprecated`
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L92)
#### assertGreaterThenOrEqual()
*protected* assertGreaterThenOrEqual($expected, $actual, $message = null)
* `deprecated`
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L112)
#### assertInstanceOf()
*protected* assertInstanceOf($class, $actual, $description = null)
* `param` $class
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L428)
#### assertInternalType()
*protected* assertInternalType($type, $actual, $description = null)
* `param` $type
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L448)
#### assertIsEmpty()
*protected* assertIsEmpty($actual, $description = null)
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L375)
#### assertLessOrEquals()
*protected* assertLessOrEquals($expected, $actual, $description = null)
* `param` $expected
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L366)
#### assertLessThan()
*protected* assertLessThan($expected, $actual, $message = null)
Checks that actual is less than expected
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L124)
#### assertLessThanOrEqual()
*protected* assertLessThanOrEqual($expected, $actual, $message = null)
Checks that actual is less or equal than expected
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L136)
#### assertNot()
*protected* assertNot($arguments)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L22)
#### assertNotContains()
*protected* assertNotContains($needle, $haystack, $message = null)
Checks that haystack doesn't contain needle.
* `param` $needle
* `param` $haystack
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L161)
#### assertNotEmpty()
*protected* assertNotEmpty($actual, $message = null)
Checks that variable is not empty.
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L232)
#### assertNotEquals()
*protected* assertNotEquals($expected, $actual, $message = null, $delta = null)
Checks that two variables are not equal
* `param` $expected
* `param` $actual
* `param string` $message
* `param float` $delta
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L48)
#### assertNotFalse()
*protected* assertNotFalse($condition, $message = null)
Checks that the condition is NOT false (everything but false)
* `param` $condition
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L298)
#### assertNotInstanceOf()
*protected* assertNotInstanceOf($class, $actual, $description = null)
* `param` $class
* `param` $actual
* `param` $description
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L438)
#### assertNotNull()
*protected* assertNotNull($actual, $message = null)
Checks that variable is not NULL
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L254)
#### assertNotRegExp()
*protected* assertNotRegExp($pattern, $string, $message = null)
Checks that string not match with pattern
* `param string` $pattern
* `param string` $string
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L185)
#### assertNotSame()
*protected* assertNotSame($expected, $actual, $message = null)
Checks that two variables are not same
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L72)
#### assertNotTrue()
*protected* assertNotTrue($condition, $message = null)
Checks that the condition is NOT true (everything but true)
* `param` $condition
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L276)
#### assertNull()
*protected* assertNull($actual, $message = null)
Checks that variable is NULL
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L243)
#### assertRegExp()
*protected* assertRegExp($pattern, $string, $message = null)
Checks that string match with pattern
* `param string` $pattern
* `param string` $string
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L173)
#### assertSame()
*protected* assertSame($expected, $actual, $message = null)
Checks that two variables are same
* `param` $expected
* `param` $actual
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L60)
#### assertStringStartsNotWith()
*protected* assertStringStartsNotWith($prefix, $string, $message = null)
Checks that a string doesn't start with the given prefix.
* `param string` $prefix
* `param string` $string
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L209)
#### assertStringStartsWith()
*protected* assertStringStartsWith($prefix, $string, $message = null)
Checks that a string starts with the given prefix.
* `param string` $prefix
* `param string` $string
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L197)
#### assertThat()
*protected* assertThat($haystack, $constraint, $message = null)
* `param` $haystack
* `param` $constraint
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L309)
#### assertThatItsNot()
*protected* assertThatItsNot($haystack, $constraint, $message = null)
Checks that haystack doesn't attend
* `param` $haystack
* `param` $constraint
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L321)
#### assertTrue()
*protected* assertTrue($condition, $message = null)
Checks that condition is positive.
* `param` $condition
* `param string` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L265)
#### debug()
*protected* debug($message)
Print debug message to the screen.
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L272)
#### debugSection()
*protected* debugSection($title, $message)
Print debug message with a title
* `param` $title
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L283)
#### fail()
*protected* fail($message)
Fails the test with message.
* `param` $message
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/Shared/Asserts.php#L458)
#### getModule()
*protected* getModule($name)
Get another module by its name:
```php
<?php
$this->getModule('WebDriver')->_findElements('.items');
```
* `param` $name
* `return` Module
* `throws` ModuleException
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L324)
#### getModules()
*protected* getModules()
Get all enabled modules
* `return` array
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L307)
#### hasModule()
*protected* hasModule($name)
Checks that module is enabled.
* `param` $name
* `return` bool
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L297)
#### onReconfigure()
*protected* onReconfigure()
HOOK to be executed when config changes with `_reconfigure`.
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L129)
#### scalarizeArray()
*protected* scalarizeArray($array)
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L349)
#### validateConfig()
*protected* validateConfig()
Validates current config for required fields and required packages.
* `throws` Exception\ModuleConfigException
* `throws` ModuleException
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php#L148)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Module.php">Help us to improve documentation. Edit module reference</a></div>

View File

@@ -0,0 +1,328 @@
## Codeception\Stub
#### *public static* make($class, $params = null, $testCase = null)
Instantiates a class without executing a constructor.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
Stub::make('User');
Stub::make('User', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::make(new User, ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::make('User', ['save' => function () { return true; }]);
Stub::make('User', ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::make('User', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class - A class to be mocked
* `param array` $params - properties and methods to set
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object - mock
* throws \RuntimeException when class does not exist
* throws \Exception
#### *public static* factory($class, $num = null, $params = null)
Creates $num instances of class through `Stub::make`.
* `param mixed` $class
* `param int` $num
* `param array` $params
* return array
* throws \Exception
#### *public static* makeEmptyExcept($class, $method, $params = null, $testCase = null)
Instantiates class having all methods replaced with dummies except one.
Constructor is not triggered.
Properties and methods can be replaced.
Even protected and private properties can be set.
``` php
<?php
Stub::makeEmptyExcept('User', 'save');
Stub::makeEmptyExcept('User', 'save', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
* Stub::makeEmptyExcept(new User, 'save');
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
Stub::makeEmptyExcept('User', 'save', ['isValid' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::makeEmptyExcept('User', 'validate', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param string` $method
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* makeEmpty($class, $params = null, $testCase = null)
Instantiates class having all methods replaced with dummies.
Constructor is not triggered.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
Stub::makeEmpty('User');
Stub::makeEmpty('User', ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
Stub::makeEmpty(new User, ['name' => 'davert']);
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::makeEmpty('User', ['save' => function () { return true; }]);
Stub::makeEmpty('User', ['save' => true));
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::makeEmpty('User', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* copy($obj, $params = null)
Clones an object and redefines it's properties (even protected and private)
* `param` $obj
* `param array` $params
* return mixed
* throws \Exception
#### *public static* construct($class, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::construct('User', ['autosave' => false]);
Stub::construct('User', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::construct(new User, ['autosave' => false), ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::construct('User', [], ['save' => function () { return true; }]);
Stub::construct('User', [], ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::construct('User', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* constructEmpty($class, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor with all methods replaced with dummies.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::constructEmpty('User', ['autosave' => false]);
Stub::constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
Stub::constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::constructEmpty('User', [], ['save' => function () { return true; }]);
Stub::constructEmpty('User', [], ['save' => true]);
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::constructEmpty('User', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
#### *public static* constructEmptyExcept($class, $method, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::constructEmptyExcept('User', 'save');
Stub::constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
Stub::constructEmptyExcept('User', 'save', [], ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::constructEmptyExcept('User', 'save', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param string` $method
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
#### *public static* update($mock, array $params)
Replaces properties of current stub
* `param \PHPUnit\Framework\MockObject\MockObject` $mock
* `param array` $params
* return mixed
* throws \LogicException
#### *public static* consecutive()
Stubbing a method call to return a list of values in the specified order.
``` php
<?php
$user = Stub::make('User', array('getName' => Stub::consecutive('david', 'emma', 'sam', 'amy')));
$user->getName(); //david
$user->getName(); //emma
$user->getName(); //sam
$user->getName(); //amy
?>
```
* return ConsecutiveMap

View File

@@ -0,0 +1,141 @@
## Codeception\Util\XmlBuilder
That's a pretty simple yet powerful class to build XML structures in jQuery-like style.
With no XML line actually written!
Uses DOM extension to manipulate XML data.
```php
<?php
$xml = new \Codeception\Util\XmlBuilder();
$xml->users
->user
->val(1)
->email
->val('davert@mail.ua')
->attr('valid','true')
->parent()
->cart
->attr('empty','false')
->items
->item
->val('useful item');
->parents('user')
->active
->val(1);
echo $xml;
```
This will produce this XML
```xml
<?xml version="1.0"?>
<users>
<user>
1
<email valid="true">davert@mail.ua</email>
<cart empty="false">
<items>
<item>useful item</item>
</items>
</cart>
<active>1</active>
</user>
</users>
```
### Usage
Builder uses chained calls. So each call to builder returns a builder object.
Except for `getDom` and `__toString` methods.
* `$xml->node` - create new xml node and go inside of it.
* `$xml->node->val('value')` - sets the inner value of node
* `$xml->attr('name','value')` - set the attribute of node
* `$xml->parent()` - go back to parent node.
* `$xml->parents('user')` - go back through all parents to `user` node.
Export:
* `$xml->getDom` - get a DOMDocument object
* `$xml->__toString` - get a string representation of XML.
[Source code](https://github.com/Codeception/Codeception/blob/master/src/Codeception/Util/XmlBuilder.php)
#### __construct()
*public* __construct()
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L80)
#### __get()
*public* __get($tag)
Appends child node
* `param` $tag
* `return` XmlBuilder
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L93)
#### __toString()
*public* __toString()
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L165)
#### attr()
*public* attr($attr, $val)
Sets attribute for current node
* `param` $attr
* `param` $val
* `return` XmlBuilder
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L120)
#### getDom()
*public* getDom()
* `return` \DOMDocument
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L173)
#### parent()
*public* parent()
Traverses to parent
* `return` XmlBuilder
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L131)
#### parents()
*public* parents($tag)
Traverses to parent with $name
* `param` $tag
* `return` XmlBuilder
* `throws` \Exception
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L145)
#### val()
*public* val($val)
* `param` $val
* `return` XmlBuilder
[See source](https://github.com/Codeception/Codeception/blob/2.4/src/Codeception/Util/XmlBuilder.php#L106)
<p>&nbsp;</p><div class="alert alert-warning">Reference is taken from the source code. <a href="https://github.com/Codeception/Codeception/blob/2.4/src//Codeception/Util/XmlBuilder.php">Help us to improve documentation. Edit module reference</a></div>