基于 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

运行所有的样例测试,报错: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

运行所有的样例测试,符合预期

图2

vendor/bin/codecept run

7、要开始编写 API 测试,创建 API 测试套件,运行命令,如图3

要开始编写 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

创建测试:获取用户列表(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

运行 build 命令,新的功能已添加到 ApiTester 类

图5

vendor/bin/codecept build -- -c api

13、/api/tests/_support/_generated/ApiTesterActions.php 成功生成,新增方法 getMinorPatch(),查看,如图6

/api/tests/_support/_generated/ApiTesterActions.php 成功生成,新增方法 getMinorPatch(),查看

图6

14、安装 flow/jsonpath ,以检查响应的结构,如图7

安装 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

运行测试,获取详细的输出,可看到一步一步的行为报告,符合预期

图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

运行测试,仅测试 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

运行所有的样例测试,符合预期

图10

vendor/bin/codecept run

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/250408.html

(0)
上一篇 2022年4月29日
下一篇 2022年4月29日

相关推荐

发表回复

登录后才能评论