基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),在 api 的 tests 目录中准备用户相关操作的一些自动化测试的样例(API 测试),确保应用程序在改变或增加新的功能时不会影响现有的功能 (四)
1、删除控制器 \api\controllers\SiteController.php,删除模型 \api\models\ContactForm.php、\api\models\LoginForm.php、\api\models\PasswordResetRequestForm.php、\api\models\ResetPasswordForm.php、\api\models\SignupForm.php,删除目录(视图相关) \api\views、\api\assets、\api\web\css
2、删除测试(单元测试、功能测试、验收测试)的相关文件,删除 \api\tests\acceptance\HomeCest.php,删除 \api\tests\functional\AboutCest.php、\api\tests\functional\ContactCest.php、\api\tests\functional\HomeCest.php、\api\tests\functional\LoginCest.php、\api\tests\functional\SignupCest.php,删除 \api\tests\unit\models\ContactFormTest.php、\api\tests\unit\models\PasswordResetRequestFormTest.php、\api\tests\unit\models\ResetPasswordFormTest.php、\api\tests\unit\models\SignupFormTest.php
3、运行所有的样例测试,报错:FAILURES! Tests: 24, Assertions: 57, Failures: 5,如图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、(三) 的第22步骤需要还原,此步骤导致样例测试报错,调整为仅web应用情况下,才支持内容协商功能
(1)还原 \common\config\main.php 在第22步骤所做的编辑
(2)编辑 \backend\config\main.php、\frontend\config\main.php
注:如果请求中没有检测到语言, 使用 [[languages]] 第一个配置项。
'bootstrap' => ['log', 'contentNegotiator'], 'components' => [ 'contentNegotiator' => [ 'class' => 'yii\filters\ContentNegotiator', 'languages' => [ 'en-US', 'zh-CN', ], ], ],
(3)编辑 \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', ], ], ],
删除
'response' => [ 'format' => yii\web\Response::FORMAT_JSON, ],
5、打开 Windows PowerShell,执行 init 命令并选择 dev 作为环境,然后重新配置数据库连接
.\init 0 yes Yes Yes Yes Yes Yes
6、运行所有的样例测试,符合预期,如图2
vendor/bin/codecept run
7、要开始编写 API 测试,创建 API 测试套件,运行命令,如图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、编辑 \api\tests\api.suite.yml 以启用该套件的模块
actor: ApiTester modules: enabled: - REST: url: /v1 depends: Yii2 - \api\tests\Helper\Api config: - Yii2
9、创建测试:获取用户列表(user/IndexEmpty),如图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]],该属性指定应用的版本,默认为’1.0’, 配置为:1.0.0,编辑 \api\config\main.php,此值应与 Git 上的 tag 保持一致
'version' => '1.0.0',
11、编辑 \api\tests\_support\Helper\Api.php,添加 获取当前版本号(次版本号.修订号) 的方法
<?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、运行 build 命令,新的功能已添加到 ApiTester 类,如图5
vendor/bin/codecept build -- -c api
13、\api\tests\_support\_generated\ApiTesterActions.php 成功生成,新增方法 getMinorPatch(),查看,如图6
14、安装 flow/jsonpath ,以检查响应的结构,如图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、需要为每个测试实现一个公共方法,让 indexIsJson 通过 REST API 测试获取用户列表(用户列表为空、JSON响应),让 indexIsXml 通过 REST API 测试获取用户列表(用户列表为空、XML响应),编辑 \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、运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期,如图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、创建测试:获取用户列表(user/Index)
vendor/bin/codecept generate:cest api user/Index -c api
18、需要为每个测试实现一个公共方法,使用 Fixtures,让 indexIsJson 通过 REST API 测试获取用户列表(获取用户列表成功、JSON响应),让 indexIsXml 通过 REST API 测试获取用户列表(获取用户列表成功、XML响应),编辑 \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、运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期
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}: 返回用户 {id} 的详细信息,创建测试:获取用户详情
vendor/bin/codecept generate:cest api user/View -c api
21、编辑 \api\tests\api\user\ViewCest.php,获取用户详情(用户ID:{id},不存在/用户ID:{id},的状态为已删除/获取用户详情成功)
<?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、运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期
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: 创建一个新用户,创建测试:创建用户
vendor/bin/codecept generate:cest api user/Create -c api
24、编辑 \api\tests\api\user\CreateCest.php,支持(创建用户成功、数据验证失败:{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、运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期
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}: 更新一个用户,复制 \api\tests\api\user\CreateCest.php 为 \api\tests\api\user\UpdateCest.php,更新用户(用户ID:{id},不存在/数据验证失败:{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 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、运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期
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}: 删除用户,复制 \api\tests\api\user\ViewCest.php 为 \api\tests\api\user\DeleteCest.php,删除用户(用户ID:{id},不存在/删除用户成功),编辑
<?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、运行测试,仅测试 api user/CreateCest.php,获取详细的输出,可看到一步一步的行为报告,符合预期,如图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: 显示关于末端 /users 支持的动词;OPTIONS /users/{id}: 显示关于末端 /users/{id} 支持的动词,复制 \api\tests\api\user\IndexCest.php 为 \api\tests\api\user\OptionsCest.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 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、运行测试,仅测试 api user/OptionsCest.php,获取详细的输出,可看到一步一步的行为报告,符合预期
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、运行所有的样例测试,符合预期,如图10
vendor/bin/codecept run
近期评论