1. Delete the controller \api\controllers\SiteController.php. Delete the models \api\models\ContactForm.php, \api\models\LoginForm.php, \api\models\PasswordResetRequestForm.php, \api\models\ResetPasswordForm.php, and \api\models\SignupForm.php. Delete the directories (view-related) \api\views, \api\assets, and \api\web\css.
2. Delete the related test files (unit tests, functional tests, acceptance tests). Delete \api\tests\acceptance\HomeCest.php. Delete \api\tests\functional\AboutCest.php, \api\tests\functional\ContactCest.php, \api\tests\functional\HomeCest.php, \api\tests\functional\LoginCest.php, and \api\tests\functional\SignupCest.php. Delete \api\tests\unit\models\ContactFormTest.php, \api\tests\unit\models\PasswordResetRequestFormTest.php, \api\tests\unit\models\ResetPasswordFormTest.php, and \api\tests\unit\models\SignupFormTest.php.
3. Run all sample tests. The error reported is: FAILURES! Tests: 24, Assertions: 57, Failures: 5, as shown in Figure 1.

Codeception PHP Testing Framework v2.4.1
Powered by PHPUnit 7.0.3 by Sebastian Bergmann and contributors.
Frontend\tests.functional Tests (12) -------------------------------------------------------------------------------------------------------------------------
+ AboutCest: Check about (0.10s)
+ ContactCest: Check contact (0.19s)
x ContactCest: Check contact submit no data (0.04s)
x ContactCest: Check contact submit not correct email (0.02s)
+ ContactCest: Check contact submit correct data (0.08s)
+ HomeCest: Check open (0.01s)
x LoginCest: Check empty (0.02s)
+ LoginCest: Check wrong password (0.03s)
+ LoginCest: Check valid login (0.58s)
x SignupCest: Signup with empty fields (0.02s)
x SignupCest: Signup with wrong email (0.03s)
+ SignupCest: Signup successfully (0.59s)
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Frontend\tests.unit Tests (8) --------------------------------------------------------------------------------------------------------------------------------
+ ContactFormTest: Send email (0.02s)
+ PasswordResetRequestFormTest: Send message with wrong email address (0.03s)
+ PasswordResetRequestFormTest: Not send emails to inactive user (0.03s)
+ PasswordResetRequestFormTest: Send email successfully (0.04s)
+ ResetPasswordFormTest: Reset wrong token (0.03s)
+ ResetPasswordFormTest: Reset correct token (0.59s)
+ SignupFormTest: Correct signup (1.14s)
+ SignupFormTest: Not correct signup (0.03s)
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Time: 4.83 seconds, Memory: 26.00MB
There were 5 failures:
---------
1) ContactCest: Check contact submit no data
Test tests\functional\ContactCest.php:checkContactSubmitNoData
Step See "Name cannot be blank",".help-block"
Fail Failed asserting that any element by '.help-block' on user /index-test.php/site/contact
+ <p class="help-block help-block-error">Name不能为空。</p>
+ <p class="help-block help-block-error">Email不能为空。</p>
+ <p class="help-block help-block-error">Subject不能为空。</p>
+ <p class="help-block help-block-error">Body不能为空。</p>
+ <p class="help-block help-block-error">验证码不正确。</p>
contains text 'Name cannot be blank'
Scenario Steps:
4. $I->see("Name cannot be blank",".help-block") at tests\_support\FunctionalTester.php:26
3. $I->see("Contact","h1") at tests\functional\ContactCest.php:23
2. $I->submitForm("#contact-form",[]) at tests\functional\ContactCest.php:22
1. $I->amOnuser(["site/contact"]) at tests\functional\ContactCest.php:12
---------
2) ContactCest: Check contact submit not correct email
Test tests\functional\ContactCest.php:checkContactSubmitNotCorrectEmail
Step See "Email is not a valid email address.",".help-block"
Fail Failed asserting that any element by '.help-block' on user /index-test.php/site/contact
+ <p class="help-block help-block-error"></p>
+ <p class="help-block help-block-error">Email不是有效的邮箱地址。</p>
+ <p class="help-block help-block-error"></p>
+ <p class="help-block help-block-error"></p>
+ <p class="help-block help-block-error"></p>
contains text 'Email is not a valid email address.'
Scenario Steps:
3. $I->see("Email is not a valid email address.",".help-block") at tests\_support\FunctionalTester.php:26
2. $I->submitForm("#contact-form",{"ContactForm[name]":"tester","ContactForm[email]":"tester.email","ContactForm[subject]":"test subject","ContactForm[b...})
at tests\functional\ContactCest.php:34
1. $I->amOnuser(["site/contact"]) at tests\functional\ContactCest.php:12
---------
3) LoginCest: Check empty
Test tests\functional\LoginCest.php:checkEmpty
Step See "Username cannot be blank.",".help-block"
Fail Failed asserting that any element by '.help-block' on user /index-test.php/site/login
+ <p class="help-block help-block-error">Username不能为空。</p>
+ <p class="help-block help-block-error">Password不能为空。</p>
+ <p class="help-block help-block-error"></p>
contains text 'Username cannot be blank.'
Scenario Steps:
3. $I->see("Username cannot be blank.",".help-block") at tests\_support\FunctionalTester.php:26
2. $I->submitForm("#login-form",{"LoginForm[username]":"","LoginForm[password]":""}) at tests\functional\LoginCest.php:42
1. $I->amOnRoute("site/login") at tests\functional\LoginCest.php:29
---------
4) SignupCest: Signup with empty fields
Test tests\functional\SignupCest.php:signupWithEmptyFields
Step See "Username cannot be blank.",".help-block"
Fail Failed asserting that any element by '.help-block' on user /index-test.php/site/signup
+ <p class="help-block help-block-error">Username不能为空。</p>
+ <p class="help-block help-block-error">Email不能为空。</p>
+ <p class="help-block help-block-error">Password不能为空。</p>
contains text 'Username cannot be blank.'
Scenario Steps:
5. $I->see("Username cannot be blank.",".help-block") at tests\_support\FunctionalTester.php:26
4. $I->submitForm("#form-signup",[]) at tests\functional\SignupCest.php:21
3. $I->see("Please fill out the following fields to signup:") at tests\functional\SignupCest.php:20
2. $I->see("Signup","h1") at tests\functional\SignupCest.php:19
1. $I->amOnRoute("site/signup") at tests\functional\SignupCest.php:14
---------
5) SignupCest: Signup with wrong email
Test tests\functional\SignupCest.php:signupWithWrongEmail
Step See "Email is not a valid email address.",".help-block"
Fail Failed asserting that any element by '.help-block' on user /index-test.php/site/signup
+ <p class="help-block help-block-error"></p>
+ <p class="help-block help-block-error">Email不是有效的邮箱地址。</p>
+ <p class="help-block help-block-error"></p>
contains text 'Email is not a valid email address.'
Scenario Steps:
5. $I->see("Email is not a valid email address.",".help-block") at tests\functional\SignupCest.php:39
4. $I->dontSee("Password cannot be blank.",".help-block") at tests\functional\SignupCest.php:38
3. $I->dontSee("Username cannot be blank.",".help-block") at tests\functional\SignupCest.php:37
2. $I->submitForm("#form-signup",{"SignupForm[username]":"tester","SignupForm[email]":"ttttt","SignupForm[password]":"tester_password"}) at tests\functional\
SignupCest.php:32
1. $I->amOnRoute("site/signup") at tests\functional\SignupCest.php:14
FAILURES!
Tests: 20, Assertions: 48, Failures: 5.vendor/bin/codecept run
4. Step 22 in section (III) needs to be reverted, as this step caused the sample tests to fail. Adjust it so that the content negotiation feature is only supported for web applications.
(1) Revert the edits made to \common\config\main.php in step 22.
(2) Edit \backend\config\main.php and \frontend\config\main.php.
Note: If no language is detected in the request, the first configured item in [[languages]] is used.
'bootstrap' => ['log', 'contentNegotiator'],
'components' => [
'contentNegotiator' => [
'class' => 'yii\filters\ContentNegotiator',
'languages' => [
'en-US',
'zh-CN',
],
],
],
(3) Edit \api\config\main.php.
'bootstrap' => ['log', 'contentNegotiator'],
'components' => [
'contentNegotiator' => [
'class' => 'yii\filters\ContentNegotiator',
'formats' => [
'application/json' => yii\web\Response::FORMAT_JSON,
'application/xml' => yii\web\Response::FORMAT_XML,
],
'languages' => [
'en-US',
'zh-CN',
],
],
],
Delete
'response' => [
'format' => yii\web\Response::FORMAT_JSON,
],
5. Open Windows PowerShell, run the init command, select dev as the environment, and then reconfigure the database connection.
.\init
0
yes
Yes
Yes
Yes
Yes
Yes
6. Run all sample tests. The results match expectations, as shown in Figure 2.

vendor/bin/codecept run
7. To start writing API tests, create an API test suite by running the command, as shown in Figure 3.

vendor/bin/codecept generate:suite api -c api
Helper \api\tests\Helper\Api was created in E:\wwwroot\github-shuijingwan-yii2-app-advanced\api\tests/_support\Helper\Api.php
Actor ApiTester was created in E:\wwwroot\github-shuijingwan-yii2-app-advanced\api\tests/_support\ApiTester.php
Suite config api.suite.yml was created.
Next steps:
1. Edit api.suite.yml to enable modules for this suite
2. Create first test with generate:cest testName ( or test|cept) command
3. Run tests of this suite with codecept run api command
Suite api generated
8. Edit \api\tests\api.suite.yml to enable the modules for this suite.
actor: ApiTester
modules:
enabled:
- REST:
url: /v1
depends: Yii2
- \api\tests\Helper\Api
config:
- Yii2
9. Create a test: get user list (user/IndexEmpty), as shown in Figure 4.

vendor/bin/codecept generate:cest api user/IndexEmpty -c api
Test was created in E:\wwwroot\github-shuijingwan-yii2-app-advanced\api\tests\api\user\IndexEmptyCest.php
10. [[yii\base\Application::version|version]]. This property specifies the application version, which defaults to ‘1.0’. Configure it as 1.0.0 by editing \api\config\main.php. This value should match the tag on Git.
'version' => '1.0.0',
11. Edit \api\tests\_support\Helper\Api.php to add a method for getting the current version number (minor version.patch version).
<?php
namespace api\tests\Helper;
use Yii;
use yii\helpers\StringHelper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Api extends \Codeception\Module
{
// 获取当前版本号(次版本号.修订号)
public function getMinorPatch() {
$version = StringHelper::explode(Yii::$app->version, '.');
return $version[1] . '.' . $version[2];
}
}12. Run the build command. The new feature has been added to the ApiTester class, as shown in Figure 5.

vendor/bin/codecept build -- -c api
13. \api\tests\_support\_generated\ApiTesterActions.php is successfully generated. The new method getMinorPatch() is added. View it, as shown in Figure 6.

14. Install flow/jsonpath to check the response structure, as shown in Figure 7.

composer require --prefer-dist flow/jsonpath
Using version ^0.4.0 for flow/jsonpath
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing flow/jsonpath (0.4.0): Downloading (100%)
Writing lock file
Generating autoload files
15. A common method needs to be implemented for each test. Let indexIsJson test getting the user list via the REST API (empty user list, JSON response). Let indexIsXml test getting the user list via the REST API (empty user list, XML response). Edit \api\tests\api\user\IndexEmptyCest.php.
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
class IndexEmptyCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
// 获取用户列表(用户列表为空、JSON响应)
public function indexIsJson(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 20001,
'message' => Yii::t('error', '20001'),
]);
}
// 获取用户列表(用户列表为空、XML响应)
public function indexIsXml(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20001]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('error', '20001')]));
}
}
16. Run the tests to get detailed output. You can see a step-by-step behavior report, which matches expectations, as shown in Figure 8.

vendor/bin/codecept run --steps -- -c api
Codeception PHP Testing Framework v2.4.1
Powered by PHPUnit 7.1.2 by Sebastian Bergmann and contributors.
Api\tests.api Tests (2) -----------------------------------------------------
IndexEmptyCest: Index is json
Signature: api\tests\user\IndexEmptyCest:indexIsJson
Test: tests\api\user\IndexEmptyCest.php:indexIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send get "/users"
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20001,"message":"User list is empty"}
PASSED
IndexEmptyCest: Index is xml
Signature: api\tests\user\IndexEmptyCest:indexIsXml
Test: tests\api\user\IndexEmptyCest.php:indexIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send get "/users"
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
-----------------------------------------------------------------------------
Api\tests.functional Tests (0) ----------------------------------------------
-----------------------------------------------------------------------------
Api\tests.unit Tests (0) ----------------------------------------------------
-----------------------------------------------------------------------------
Time: 1.11 seconds, Memory: 16.00MB
OK (2 tests, 12 assertions)
17. Create a test: get user list (user/Index).
vendor/bin/codecept generate:cest api user/Index -c api
18. A common method needs to be implemented for each test using Fixtures. Let indexIsJson test getting the user list via the REST API (successfully getting user list, JSON response). Let indexIsXml test getting the user list via the REST API (successfully getting user list, XML response). Edit \api\tests\api\user\IndexCest.php.
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class IndexCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 获取用户列表(获取用户列表成功、JSON响应)
public function indexIsJson(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
$I->seeResponseJsonMatchesJsonPath('$.data');
$I->seeResponseJsonMatchesJsonPath('$.data.items');
$I->seeResponseJsonMatchesJsonPath('$.data._links');
$I->seeResponseJsonMatchesJsonPath('$.data._meta');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 10000,
'message' => Yii::t('success', '10001'),
]);
}
// 获取用户列表(获取用户列表成功、XML响应)
public function indexIsXml(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
$I->seeXmlResponseMatchesXpath('//data');
$I->seeXmlResponseMatchesXpath('//data/items');
$I->seeXmlResponseMatchesXpath('//data/_links');
$I->seeXmlResponseMatchesXpath('//data/_meta');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 10000]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('success', '10001')]));
}
}
19. Run the tests to get detailed output. You can see a step-by-step behavior report, which matches expectations.
vendor/bin/codecept run --steps -- -c api
IndexCest: Index is json
Signature: api\tests\user\IndexCest:indexIsJson
Test: tests\api\user\IndexCest.php:indexIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send get "/users"
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response json matches json path "$.data"
I see response json matches json path "$.data.items"
I see response json matches json path "$.data._links"
I see response json matches json path "$.data._meta"
I see response contains json {"code":10000,"message":"Get user list success"}
PASSED
IndexCest: Index is xml
Signature: api\tests\user\IndexCest:indexIsXml
Test: tests\api\user\IndexCest.php:indexIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send get "/users"
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response matches xpath "//data"
I see xml response matches xpath "//data/items"
I see xml response matches xpath "//data/_links"
I see xml response matches xpath "//data/_meta"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
20. GET /users/{id}: Returns the details of user {id}. Create a test: get user details.
vendor/bin/codecept generate:cest api user/View -c api
21. Edit \api\tests\api\user\ViewCest.php to get user details (user ID: {id}, does not exist / user ID: {id}, status is deleted / successfully getting user details).
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class ViewCest
{
const STATUS_DELETED = 0; //状态:已删除
const STATUS_ACTIVE = 10; //状态:活跃
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 获取用户详情(获取用户详情成功、JSON响应)
public function viewIsJson(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
$I->seeResponseJsonMatchesJsonPath('$.data');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 10000,
'message' => Yii::t('success', '10002'),
]);
}
// 获取用户详情(获取用户详情成功、XML响应)
public function viewIsXml(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
$I->seeXmlResponseMatchesXpath('//data');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 10000]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('success', '10002')]));
}
// 获取用户详情,使用不存在的ID(用户ID:{id},不存在、JSON响应)
public function viewWithNotExistIdIsJson(ApiTester $I)
{
$id = 9999;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 20002,
'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20002'), ['id' => $id])),
]);
}
// 获取用户详情,使用不存在的ID(用户ID:{id},不存在、XML响应)
public function viewWithNotExistIdIsXml(ApiTester $I)
{
$id = 9999;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20002]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('error', Yii::t('error', Yii::t('error', '20002'), ['id' => $id]))]));
}
// 获取用户详情(用户ID:{id},的状态为已删除、JSON响应)
public function viewStatusDeletedIsJson(ApiTester $I)
{
$id = 2;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 20003,
'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20003'), ['id' => $id])),
]);
}
// 获取用户详情(用户ID:{id},的状态为已删除、XML响应)
public function viewStatusDeletedIsXml(ApiTester $I)
{
$id = 2;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendGET('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20003]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('error', Yii::t('error', Yii::t('error', '20003'), ['id' => $id]))]));
}
}
22. Run the tests to get detailed output. You can see a step-by-step behavior report, which matches expectations.
vendor/bin/codecept run --steps -- -c api
ViewCest: View is json
Signature: api\tests\user\ViewCest:viewIsJson
Test: tests\api\user\ViewCest.php:viewIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send get "/users/1"
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response json matches json path "$.data"
I see response contains json {"code":10000,"message":"Get user details success"}
PASSED
ViewCest: View is xml
Signature: api\tests\user\ViewCest:viewIsXml
Test: tests\api\user\ViewCest.php:viewIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send get "/users/1"
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response matches xpath "//data"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
ViewCest: View with not exist id is json
Signature: api\tests\user\ViewCest:viewWithNotExistIdIsJson
Test: tests\api\user\ViewCest.php:viewWithNotExistIdIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send get "/users/9999"
I see response code is 404
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20002,"message":"User ID: 9999, does not exist"}
PASSED
ViewCest: View with not exist id is xml
Signature: api\tests\user\ViewCest:viewWithNotExistIdIsXml
Test: tests\api\user\ViewCest.php:viewWithNotExistIdIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send get "/users/9999"
I see response code is 404
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
ViewCest: View status deleted is json
Signature: api\tests\user\ViewCest:viewStatusDeletedIsJson
Test: tests\api\user\ViewCest.php:viewStatusDeletedIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send get "/users/2"
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20003,"message":"User ID: 2, status is deleted"}
PASSED
ViewCest: View status deleted is xml
Signature: api\tests\user\ViewCest:viewStatusDeletedIsXml
Test: tests\api\user\ViewCest.php:viewStatusDeletedIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send get "/users/2"
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
23. POST /users: Create a new user. Create a test: create user.
vendor/bin/codecept generate:cest api user/Create -c api
24. Edit \api\tests\api\user\CreateCest.php to support (successfully creating user / data validation failed: {firstErrors}).
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class CreateCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 创建用户(创建用户成功、JSON响应)
public function createIsJson(ApiTester $I)
{
$data = [
'username' => '111111',
'email' => '111111@163.com',
'password' => '111111',
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::CREATED); // 201
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
$I->seeResponseJsonMatchesJsonPath('$.data');
$I->seeResponseJsonMatchesJsonPath('$.data.username');
$I->seeResponseJsonMatchesJsonPath('$.data.email');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 10000,
'message' => Yii::t('success', '10003'),
'data' => [
'username' => $data['username'],
'email' => $data['email'],
],
]);
}
// 创建用户(创建用户成功、XML响应)
public function createIsXml(ApiTester $I)
{
$data = [
'username' => '111111',
'email' => '111111@163.com',
'password' => '111111',
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::CREATED); // 201
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
$I->seeXmlResponseMatchesXpath('//data');
$I->seeXmlResponseMatchesXpath('//data//username');
$I->seeXmlResponseMatchesXpath('//data//email');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 10000]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('success', '10003')]));
$I->seeXmlResponseIncludes(Xml::toXml(['username' => $data['username']]));
$I->seeXmlResponseIncludes(Xml::toXml(['email' => $data['email']]));
}
// 创建用户,使用空的字段值(数据验证失败:{firstErrors}、JSON响应)
public function createWithEmptyFieldsIsJson(ApiTester $I)
{
$data = [];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Username cannot be blank.']);
}
// 创建用户,使用空的字段值(数据验证失败:{firstErrors}、XML响应)
public function createWithEmptyFieldsIsXml(ApiTester $I)
{
$data = [];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Username cannot be blank.']));
}
// 创建用户,使用错误的邮箱(数据验证失败:{firstErrors}、JSON响应)
public function createWithWrongEmailIsJson(ApiTester $I)
{
$data = [
'username' => '111111',
'email' => '111111',
'password' => '111111',
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Email is not a valid email address.']);
}
// 创建用户,使用错误的邮箱(数据验证失败:{firstErrors}、XML响应)
public function createWithWrongEmailIsXml(ApiTester $I)
{
$data = [
'username' => '111111',
'email' => '111111',
'password' => '111111',
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Email is not a valid email address.']));
}
// 创建用户,使用已经存在的字段值(数据验证失败:{firstErrors}、JSON响应)
public function createWithExistFieldsIsJson(ApiTester $I)
{
$data = [
'username' => 'troy.becker',
'email' => 'nicolas.dianna@hotmail.com',
'password' => 'some_password',
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Username "troy.becker" has already been taken.']);
}
// 创建用户,使用已经存在的字段值(数据验证失败:{firstErrors}、XML响应)
public function createWithExistFieldsIsXml(ApiTester $I)
{
$data = [
'username' => 'troy.becker',
'email' => 'nicolas.dianna@hotmail.com',
'password' => 'some_password',
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPOST('/users', $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Username "troy.becker" has already been taken.']));
}
}
25. Run the tests to get detailed output. You can see a step-by-step behavior report, which matches expectations.
vendor/bin/codecept run --steps -- -c api
CreateCest: Create is json
Signature: api\tests\user\CreateCest:createIsJson
Test: tests\api\user\CreateCest.php:createIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send post "/users",{"username":"111111","email":"111111@163.com","password":"111111"}
I see response code is 201
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response json matches json path "$.data"
I see response json matches json path "$.data.username"
I see response json matches json path "$.data.email"
I see response contains json {"code":10000,"message":"Create user success","data":{"username":"111111","email":"111...}
PASSED
CreateCest: Create is xml
Signature: api\tests\user\CreateCest:createIsXml
Test: tests\api\user\CreateCest.php:createIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send post "/users",{"username":"111111","email":"111111@163.com","password":"111111"}
I see response code is 201
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response matches xpath "//data"
I see xml response matches xpath "//data//username"
I see xml response matches xpath "//data//email"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
CreateCest: Create with empty fields is json
Signature: api\tests\user\CreateCest:createWithEmptyFieldsIsJson
Test: tests\api\user\CreateCest.php:createWithEmptyFieldsIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send post "/users",[]
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Username cannot be blank."}
PASSED
CreateCest: Create with empty fields is xml
Signature: api\tests\user\CreateCest:createWithEmptyFieldsIsXml
Test: tests\api\user\CreateCest.php:createWithEmptyFieldsIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send post "/users",[]
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
CreateCest: Create with wrong email is json
Signature: api\tests\user\CreateCest:createWithWrongEmailIsJson
Test: tests\api\user\CreateCest.php:createWithWrongEmailIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send post "/users",{"username":"111111","email":"111111","password":"111111"}
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Email is not a valid email address."}
PASSED
CreateCest: Create with wrong email is xml
Signature: api\tests\user\CreateCest:createWithWrongEmailIsXml
Test: tests\api\user\CreateCest.php:createWithWrongEmailIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send post "/users",{"username":"111111","email":"111111","password":"111111"}
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
CreateCest: Create with exist fields is json
Signature: api\tests\user\CreateCest:createWithExistFieldsIsJson
Test: tests\api\user\CreateCest.php:createWithExistFieldsIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send post "/users",{"username":"troy.becker","email":"nicolas.dianna@hotmail.com","password":"some_password"}
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Username "troy.becker" has already been taken."}
PASSED
CreateCest: Create with exist fields is xml
Signature: api\tests\user\CreateCest:createWithExistFieldsIsXml
Test: tests\api\user\CreateCest.php:createWithExistFieldsIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send post "/users",{"username":"troy.becker","email":"nicolas.dianna@hotmail.com","password":"some_password"}
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
26. PUT /users/{id}: Update a user. Copy \api\tests\api\user\CreateCest.php to \api\tests\api\user\UpdateCest.php. Update user (user ID: {id}, does not exist / data validation failed: {firstErrors} / successfully updating user). Edit.
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class UpdateCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 更新用户(更新用户成功、JSON响应)
public function updateIsJson(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
$I->seeResponseJsonMatchesJsonPath('$.data');
$I->seeResponseJsonMatchesJsonPath('$.data.email');
$I->seeResponseJsonMatchesJsonPath('$.data.status');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 10000,
'message' => Yii::t('success', '10004'),
'data' => [
'email' => $data['email'],
'status' => $data['status'],
],
]);
}
// 更新用户(更新用户成功、XML响应)
public function updateIsXml(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
$I->seeXmlResponseMatchesXpath('//data');
$I->seeXmlResponseMatchesXpath('//data//email');
$I->seeXmlResponseMatchesXpath('//data//status');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 10000]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('success', '10004')]));
$I->seeXmlResponseIncludes(Xml::toXml(['email' => $data['email']]));
$I->seeXmlResponseIncludes(Xml::toXml(['status' => $data['status']]));
}
// 更新用户,使用不存在的ID(数据验证失败:{firstErrors}、JSON响应)
public function updateWithNotExistIdIsJson(ApiTester $I)
{
$id = 9999;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20002]);
$I->seeResponseContainsJson(['message' => 'User ID: 9999, does not exist']);
}
// 更新用户,使用不存在的ID(数据验证失败:{firstErrors}、JSON响应)
public function updateWithNotExistIdIsXml(ApiTester $I)
{
$id = 9999;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20002]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'User ID: 9999, does not exist']));
}
// 更新用户,使用空的字段值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithEmptyFieldsIsJson(ApiTester $I)
{
$id = 1;
$data = [];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Email cannot be blank.']);
}
// 更新用户,使用空的字段值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithEmptyFieldsIsXml(ApiTester $I)
{
$id = 1;
$data = [];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Email cannot be blank.']));
}
// 更新用户,使用错误的邮箱(数据验证失败:{firstErrors}、JSON响应)
public function updateWithWrongEmailIsJson(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Email is not a valid email address.']);
}
// 更新用户,使用错误的邮箱(数据验证失败:{firstErrors}、JSON响应)
public function updateWithWrongEmailIsXml(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Email is not a valid email address.']));
}
// 更新用户,使用已经存在的字段值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithExistFieldsIsJson(ApiTester $I)
{
$id = 1;
$data = [
'email' => 'nicolas.dianna@hotmail.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Email "nicolas.dianna@hotmail.com" has already been taken.']);
}
// 更新用户,使用已经存在的字段值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithExistFieldsIsXml(ApiTester $I)
{
$id = 1;
$data = [
'email' => 'nicolas.dianna@hotmail.com',
'password' => '111111',
'status' => 0,
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Email "nicolas.dianna@hotmail.com" has already been taken.']));
}
// 更新用户,使用超出范围的状态值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithNotRangeStatusIsJson(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 5,
];
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson(['code' => 20004]);
$I->seeResponseContainsJson(['message' => 'Data validation failed: Status is invalid.']);
}
// 更新用户,使用超出范围的状态值(数据验证失败:{firstErrors}、JSON响应)
public function updateWithNotRangeStatusIsXml(ApiTester $I)
{
$id = 1;
$data = [
'email' => '111111@163.com',
'password' => '111111',
'status' => 5,
];
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendPUT('/users/' . $id, $data);
$I->seeResponseCodeIs(HttpCode::UNPROCESSABLE_ENTITY); // 422
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20004]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => 'Data validation failed: Status is invalid.']));
}
}
27. Run the tests to get detailed output. You can see a step-by-step behavior report, which matches expectations.
vendor/bin/codecept run --steps -- -c api
UpdateCest: Update is json
Signature: api\tests\user\UpdateCest:updateIsJson
Test: tests\api\user\UpdateCest.php:updateIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/1",{"email":"111111@163.com","password":"111111","status":0}
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response json matches json path "$.data"
I see response json matches json path "$.data.email"
I see response json matches json path "$.data.status"
I see response contains json {"code":10000,"message":"Update user success","data":{"email":"111111@163.com","status...}
PASSED
UpdateCest: Update is xml
Signature: api\tests\user\UpdateCest:updateIsXml
Test: tests\api\user\UpdateCest.php:updateIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/1",{"email":"111111@163.com","password":"111111","status":0}
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response matches xpath "//data"
I see xml response matches xpath "//data//email"
I see xml response matches xpath "//data//status"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
UpdateCest: Update with not exist id is json
Signature: api\tests\user\UpdateCest:updateWithNotExistIdIsJson
Test: tests\api\user\UpdateCest.php:updateWithNotExistIdIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/9999",{"email":"111111@163.com","password":"111111","status":0}
I see response code is 404
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20002}
I see response contains json {"message":"User ID: 9999, does not exist"}
PASSED
UpdateCest: Update with not exist id is xml
Signature: api\tests\user\UpdateCest:updateWithNotExistIdIsXml
Test: tests\api\user\UpdateCest.php:updateWithNotExistIdIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/9999",{"email":"111111@163.com","password":"111111","status":0}
I see response code is 404
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
UpdateCest: Update with empty fields is json
Signature: api\tests\user\UpdateCest:updateWithEmptyFieldsIsJson
Test: tests\api\user\UpdateCest.php:updateWithEmptyFieldsIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/1",[]
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Email cannot be blank."}
PASSED
UpdateCest: Update with empty fields is xml
Signature: api\tests\user\UpdateCest:updateWithEmptyFieldsIsXml
Test: tests\api\user\UpdateCest.php:updateWithEmptyFieldsIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/1",[]
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
UpdateCest: Update with wrong email is json
Signature: api\tests\user\UpdateCest:updateWithWrongEmailIsJson
Test: tests\api\user\UpdateCest.php:updateWithWrongEmailIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/1",{"email":"111111","password":"111111","status":0}
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Email is not a valid email address."}
PASSED
UpdateCest: Update with wrong email is xml
Signature: api\tests\user\UpdateCest:updateWithWrongEmailIsXml
Test: tests\api\user\UpdateCest.php:updateWithWrongEmailIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/1",{"email":"111111","password":"111111","status":0}
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
UpdateCest: Update with exist fields is json
Signature: api\tests\user\UpdateCest:updateWithExistFieldsIsJson
Test: tests\api\user\UpdateCest.php:updateWithExistFieldsIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/1",{"email":"nicolas.dianna@hotmail.com","password":"111111","status":0}
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Email "nicolas.dianna@hotmail.com" has already bee...}
PASSED
UpdateCest: Update with exist fields is xml
Signature: api\tests\user\UpdateCest:updateWithExistFieldsIsXml
Test: tests\api\user\UpdateCest.php:updateWithExistFieldsIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/1",{"email":"nicolas.dianna@hotmail.com","password":"111111","status":0}
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
UpdateCest: Update with not range status is json
Signature: api\tests\user\UpdateCest:updateWithNotRangeStatusIsJson
Test: tests\api\user\UpdateCest.php:updateWithNotRangeStatusIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send put "/users/1",{"email":"111111@163.com","password":"111111","status":5}
I see response code is 422
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20004}
I see response contains json {"message":"Data validation failed: Status is invalid."}
PASSED
UpdateCest: Update with not range status is xml
Signature: api\tests\user\UpdateCest:updateWithNotRangeStatusIsXml
Test: tests\api\user\UpdateCest.php:updateWithNotRangeStatusIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send put "/users/1",{"email":"111111@163.com","password":"111111","status":5}
I see response code is 422
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
28. DELETE /users/{id}: Delete a user. Copy \api\tests\api\user\ViewCest.php to \api\tests\api\user\DeleteCest.php. Delete user (user ID: {id}, does not exist / successfully deleting user). Edit.
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class DeleteCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 删除用户(删除用户成功、JSON响应)
public function deleteIsJson(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendDELETE('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 10000,
'message' => Yii::t('success', '10005'),
]);
}
// 删除用户(删除用户成功、XML响应)
public function deleteIsXml(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendDELETE('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 10000]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('success', '10005')]));
}
// 获取用户详情,使用不存在的ID(用户ID:{id},不存在、JSON响应)
public function deleteWithNotExistIdIsJson(ApiTester $I)
{
$id = 9999;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendDELETE('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsJson();
// 检查响应的结构
$I->seeResponseJsonMatchesJsonPath('$.code');
$I->seeResponseJsonMatchesJsonPath('$.message');
// 检查响应的数据
$I->seeResponseContainsJson([
'code' => 20002,
'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20002'), ['id' => $id])),
]);
}
// 获取用户详情,使用不存在的ID(用户ID:{id},不存在、XML响应)
public function deleteWithNotExistIdIsXml(ApiTester $I)
{
$id = 9999;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendDELETE('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::NOT_FOUND); // 404
$I->seeResponseIsXml();
// 检查响应的结构
$I->seeXmlResponseMatchesXpath('//code');
$I->seeXmlResponseMatchesXpath('//message');
// 检查响应的数据
$I->seeXmlResponseIncludes(Xml::toXml(['code' => 20002]));
$I->seeXmlResponseIncludes(Xml::toXml(['message' => Yii::t('error', Yii::t('error', Yii::t('error', '20002'), ['id' => $id]))]));
}
}
29. Run the tests, testing only api user/CreateCest.php. Get detailed output. You can see a step-by-step behavior report, which matches expectations, as shown in Figure 9.

vendor/bin/codecept run --steps -- -c api api user/DeleteCest.php
Codeception PHP Testing Framework v2.4.1
Powered by PHPUnit 7.1.2 by Sebastian Bergmann and contributors.
Api\tests.api Tests (4) ------------------------------------------------------------------------------------------------
DeleteCest: Delete is json
Signature: api\tests\user\DeleteCest:deleteIsJson
Test: tests\api\user\DeleteCest.php:deleteIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send delete "/users/1"
I see response code is 200
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":10000,"message":"Delete user success"}
PASSED
DeleteCest: Delete is xml
Signature: api\tests\user\DeleteCest:deleteIsXml
Test: tests\api\user\DeleteCest.php:deleteIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send delete "/users/1"
I see response code is 200
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
DeleteCest: Delete with not exist id is json
Signature: api\tests\user\DeleteCest:deleteWithNotExistIdIsJson
Test: tests\api\user\DeleteCest.php:deleteWithNotExistIdIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send delete "/users/9999"
I see response code is 404
I see response is json
I see response json matches json path "$.code"
I see response json matches json path "$.message"
I see response contains json {"code":20002,"message":"User ID: 9999, does not exist"}
PASSED
DeleteCest: Delete with not exist id is xml
Signature: api\tests\user\DeleteCest:deleteWithNotExistIdIsXml
Test: tests\api\user\DeleteCest.php:deleteWithNotExistIdIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send delete "/users/9999"
I see response code is 404
I see response is xml
I see xml response matches xpath "//code"
I see xml response matches xpath "//message"
I see xml response includes "DOMDocument"
I see xml response includes "DOMDocument"
PASSED
------------------------------------------------------------------------------------------------------------------------
Time: 1.25 seconds, Memory: 16.00MB
OK (4 tests, 24 assertions)
30. OPTIONS /users: Show the verbs supported by the endpoint /users; OPTIONS /users/{id}: Show the verbs supported by the endpoint /users/{id}. Copy \api\tests\api\user\IndexCest.php to \api\tests\api\user\OptionsCest.php. Edit.
<?php
namespace api\tests\user;
use Yii;
use api\tests\ApiTester;
use Codeception\Util\HttpCode;
use Codeception\Util\Xml;
use api\fixtures\UserFixture;
class OptionsCest
{
public function _before(ApiTester $I)
{
}
public function _after(ApiTester $I)
{
}
/**
* @return array
*/
public function _fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
]
];
}
// 显示关于末端 /users 支持的动词(JSON响应)
public function optionsIsJson(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendOPTIONS('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
// 检查响应的数据
$I->seeHttpHeader('Allow', 'GET, POST, HEAD, OPTIONS');
$I->seeResponseEquals('');
}
// 显示关于末端 /users 支持的动词(XML响应)
public function optionsIsXml(ApiTester $I)
{
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendOPTIONS('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
// 检查响应的数据
$I->seeHttpHeader('Allow', 'GET, POST, HEAD, OPTIONS');
$I->seeResponseEquals('');
}
// 显示关于末端 /users/{id} 支持的动词(JSON响应)
public function optionsIdIsJson(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/json; version=' . $I->getMinorPatch() . '');
$I->sendOPTIONS('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
// 检查响应的数据
$I->seeHttpHeader('Allow', 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS');
$I->seeResponseEquals('');
}
// 显示关于末端 /users/{id} 支持的动词(XML响应)
public function optionsIdIsXml(ApiTester $I)
{
$id = 1;
$I->haveHttpHeader('Accept', 'application/xml; version=' . $I->getMinorPatch() . '');
$I->sendOPTIONS('/users/' . $id);
$I->seeResponseCodeIs(HttpCode::OK); // 200
// 检查响应的数据
$I->seeHttpHeader('Allow', 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS');
$I->seeResponseEquals('');
}
}
31. Run the tests, testing only api user/OptionsCest.php. Get detailed output. You can see a step-by-step behavior report, which matches expectations.
vendor/bin/codecept run --steps -- -c api api user/OptionsCest.php
Codeception PHP Testing Framework v2.4.1
Powered by PHPUnit 7.1.2 by Sebastian Bergmann and contributors.
Api\tests.api Tests (4) ------------------------------------------------------------------------------------------------
OptionsCest: Options is json
Signature: api\tests\user\OptionsCest:optionsIsJson
Test: tests\api\user\OptionsCest.php:optionsIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send options "/users"
I see response code is 200
I see http header "Allow","GET, POST, HEAD, OPTIONS"
I see response equals ""
PASSED
OptionsCest: Options is xml
Signature: api\tests\user\OptionsCest:optionsIsXml
Test: tests\api\user\OptionsCest.php:optionsIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send options "/users"
I see response code is 200
I see http header "Allow","GET, POST, HEAD, OPTIONS"
I see response equals ""
PASSED
OptionsCest: Options id is json
Signature: api\tests\user\OptionsCest:optionsIdIsJson
Test: tests\api\user\OptionsCest.php:optionsIdIsJson
Scenario --
I get minor patch
I have http header "Accept","application/json; version=0.0"
I send options "/users/1"
I see response code is 200
I see http header "Allow","GET, PUT, PATCH, DELETE, HEAD, OPTIONS"
I see response equals ""
PASSED
OptionsCest: Options id is xml
Signature: api\tests\user\OptionsCest:optionsIdIsXml
Test: tests\api\user\OptionsCest.php:optionsIdIsXml
Scenario --
I get minor patch
I have http header "Accept","application/xml; version=0.0"
I send options "/users/1"
I see response code is 200
I see http header "Allow","GET, PUT, PATCH, DELETE, HEAD, OPTIONS"
I see response equals ""
PASSED
------------------------------------------------------------------------------------------------------------------------
Time: 1.19 seconds, Memory: 14.00MB
OK (4 tests, 12 assertions)
32. Run all sample tests. The results match expectations, as shown in Figure 10.

vendor/bin/codecept run
