tests – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Mon, 25 May 2026 13:22:47 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 Lighthouse 5 中,基于 PHPUnit 编写 Mutation 的测试 https://www.shuijingwanwq.com/2022/04/01/6233/ https://www.shuijingwanwq.com/2022/04/01/6233/#respond Fri, 01 Apr 2022 01:27:44 +0000 https://www.shuijingwanwq.com/?p=6233 浏览量: 87

1、在 Altair GraphQl Client 中运行成功。如图1

在 Altair GraphQl Client 中运行成功。

图1


mutation CreateThemeAsset {
  onlineStoreThemeAssetCreate(
    input: { themeId: "vogue", content: "string", key: "js/20220307.4.js" }
  ) {
    themeAsset {
      themeId
      key
      content
    }
  }
}





{
  "data": {
    "onlineStoreThemeAssetCreate": {
      "themeAsset": {
        "themeId": "vogue",
        "key": "js/20220307.4.js",
        "content": "string"
      }
    }
  }
}


2、编辑测试文件,/Modules/ThemeStore/Tests/Functional/GraphQl/MutationThemeAssetGraphQlApiTest.php。

<?php
 
namespace Modules\ThemeStore\Tests\Functional\GraphQl;
 
use Nuwave\Lighthouse\Testing\MakesGraphQLRequests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Nuwave\Lighthouse\Testing\ClearsSchemaCache;
use Sentry\Util\JSON;
use Tests\CreatesApplication;
 
class MutationThemeAssetGraphQlApiTest extends BaseTestCase
{
    use CreatesApplication,
        ClearsSchemaCache,
        MakesGraphQLRequests;
 
    public function testCreateThemeAsset(): void
    {
 
        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/20220307.8.js',
        ];
 
        $response = $this->graphQL('
            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {
                onlineStoreThemeAssetCreate(input: $input) {
                    themeAsset {
                        themeId
                        key
                        content
                    }
                }
            }
        ', [
            'input' => json_encode($input)
        ]);
 
        $response->assertJson(
            [
                'data' => [
                    'onlineStoreThemeAssetCreate' => [
                        'themeAsset' => $input
                    ],
                ],
            ]
        );
    }
 
    protected function setUp(): void
    {
        parent::setUp();
 
        $this->bootClearsSchemaCache();
    }
}
 
 
 

3、运行测试用例,报错:Expected type ThemeAssetCreateInput to be an object.。如图2

运行测试用例,报错:Expected type ThemeAssetCreateInput to be an object.。

图2


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 2.89 seconds, Memory: 16.00 MB

There was 1 failure:

1) Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset
Unable to find JSON:

[{
    "data": {
        "onlineStoreThemeAssetCreate": {
            "themeAsset": {
                "themeId": "vogue",
                "content": "string",
                "key": "js/20220307.8.js"
            }
        }
    }
}]

within response JSON:

[{
    "errors": [
        {
            "message": "Variable \"$input\" got invalid value \"{\"themeId\":\"vogue\",\"content\":\"string\",\"key\":\"js\\/20220307.8.js\"}\"; Expected type ThemeAssetCreateInput to be an object.",
            "extensions": {
                "category": "graphql"
            },
            "locations": [
                {
                    "line": 2,
                    "column": 39
                }
            ]
        }
    ]
}].


Failed asserting that an array has the subset Array &0 (
    'data' => Array &1 (
        'onlineStoreThemeAssetCreate' => Array &2 (
            'themeAsset' => Array &3 (
                'themeId' => 'vogue'
                'content' => 'string'
                'key' => 'js/20220307.8.js'
            )
        )
    )
).
--- Expected
+++ Actual
@@ @@
       ),
     ),
   ),
-  'data' =>
-  array (
-    'onlineStoreThemeAssetCreate' =>
-    array (
-      'themeAsset' =>
-      array (
-        'themeId' => 'vogue',
-        'content' => 'string',
-        'key' => 'js/20220307.8.js',
-      ),
-    ),
-  ),
 )

E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Assert.php:108
E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:479
E:\wwwroot\object\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php:56

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
PS E:\wwwroot\object>


4、重新编辑测试文件

<?php
 
namespace Modules\ThemeStore\Tests\Functional\GraphQl;
 
use Nuwave\Lighthouse\Testing\MakesGraphQLRequests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Nuwave\Lighthouse\Testing\ClearsSchemaCache;
use Sentry\Util\JSON;
use Tests\CreatesApplication;
 
class MutationThemeAssetGraphQlApiTest extends BaseTestCase
{
    use CreatesApplication,
        ClearsSchemaCache,
        MakesGraphQLRequests;
 
    public function testCreateThemeAsset(): void
    {
        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/20220307.9.js',
        ];
 
        $response = $this->graphQL('
            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {
                onlineStoreThemeAssetCreate(input: $input) {
                    themeAsset {
                        themeId
                        key
                        content
                    }
                }
            }
        ', [
            'input' => json_decode(json_encode($input))
        ]);
 
        $response->assertJson(
            [
                'data' => [
                    'onlineStoreThemeAssetCreate' => [
                        'themeAsset' => $input
                    ],
                ],
            ]
        );
    }
 
    protected function setUp(): void
    {
        parent::setUp();
 
        $this->bootClearsSchemaCache();
    }
}
 

5、再次运行测试,测试通过。


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 2.61 seconds, Memory: 16.00 MB

OK (1 test, 1 assertion)


6、再运行一次测试,测试未通过。原因在于同样的请求参数,不允许重复,否则验证失败,进而测试未通过断言。


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 2.77 seconds, Memory: 18.00 MB

There was 1 failure:

1) Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset
Unable to find JSON:

[{
    "data": {
        "onlineStoreThemeAssetCreate": {
            "themeAsset": {
                "themeId": "vogue",
                "content": "string",
                "key": "js/20220307.9.js"
            }
        }
    }
}]

within response JSON:

[{
    "errors": [
        {
            "message": "Validation failed for the field [onlineStoreThemeAssetCreate].",
            "extensions": {
                "validation": {
                    "input.key": [
                        "The input.key has already been taken"
                    ]
                },
                "category": "validation"
            },
            "locations": [
                {
                    "line": 3,
                    "column": 17
                }
            ],
            "path": [
                "onlineStoreThemeAssetCreate"
            ],
            "trace": [
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\ArgTraversalDirective.php",
                    "line": 29,
                    "call": "Nuwave\\Lighthouse\\Validation\\ValidateDirective::Nuwave\\Lighthouse\\Validation\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\TrimDirective.php",
                    "line": 56,
                    "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ArgTraversalDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Factories\\FieldFactory.php",
                    "line": 99,
                    "call": "Nuwave\\Lighthouse\\Schema\\Directives\\TrimDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 623,
                    "call": "Nuwave\\Lighthouse\\Schema\\Factories\\FieldFactory::Nuwave\\Lighthouse\\Schema\\Factories\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 550,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 474,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 857,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeAssetCreate')"
                },
                {
                    "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeAssetCreate')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 859,
                    "function": "array_reduce(array(1), instance of Closure, array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 490,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 263,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 215,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\Executor.php",
                    "line": 156,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
                    "line": 162,
                    "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
                    "line": 94,
                    "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null, array(29))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 268,
                    "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null, array(29))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 203,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeParsedQuery(instance of GraphQL\\Language\\AST\\DocumentNode, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 162,
                    "call": "Nuwave\\Lighthouse\\GraphQL::parseAndExecuteQuery('\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\n                onlineStoreThemeAssetCreate(input: $input) {\n                    themeAsset {\n                        themeId\n                        key\n                        content\n                    }\n                }\n            }\n        ', instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 121,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeOperation(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Utils.php",
                    "line": 99,
                    "call": "Nuwave\\Lighthouse\\GraphQL::Nuwave\\Lighthouse\\{closure}(instance of GraphQL\\Server\\OperationParams)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 120,
                    "call": "Nuwave\\Lighthouse\\Support\\Utils::applyEach(instance of Closure, instance of GraphQL\\Server\\OperationParams)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Controllers\\GraphQLController.php",
                    "line": 32,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeOperationOrOperations(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
                    "line": 48,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController::__invoke(instance of Illuminate\\Http\\Request, instance of Nuwave\\Lighthouse\\GraphQL, instance of Illuminate\\Events\\Dispatcher, instance of Laragraph\\Utils\\RequestParser, instance of Nuwave\\Lighthouse\\Execution\\SingleResponse, instance of Nuwave\\Lighthouse\\Execution\\ContextFactory)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
                    "line": 219,
                    "call": "Illuminate\\Routing\\ControllerDispatcher::dispatch(instance of Illuminate\\Routing\\Route, instance of Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController, '__invoke')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
                    "line": 176,
                    "call": "Illuminate\\Routing\\Route::runController()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 681,
                    "call": "Illuminate\\Routing\\Route::run()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 130,
                    "call": "Illuminate\\Routing\\Router::Illuminate\\Routing\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AttemptAuthentication.php",
                    "line": 34,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AcceptJson.php",
                    "line": 27,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AcceptJson::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 105,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 683,
                    "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 658,
                    "call": "Illuminate\\Routing\\Router::runRouteWithinStack(instance of Illuminate\\Routing\\Route, instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 624,
                    "call": "Illuminate\\Routing\\Router::runRoute(instance of Illuminate\\Http\\Request, instance of Illuminate\\Routing\\Route)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 613,
                    "call": "Illuminate\\Routing\\Router::dispatchToRoute(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 170,
                    "call": "Illuminate\\Routing\\Router::dispatch(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 130,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::Illuminate\\Foundation\\Http\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php",
                    "line": 60,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\ChangeAppUrlMiddleware.php",
                    "line": 23,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "App\\Http\\Middleware\\ChangeAppUrlMiddleware::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
                    "line": 21,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
                    "line": 21,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
                    "line": 27,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode.php",
                    "line": 63,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\fideloper\\proxy\\src\\TrustProxies.php",
                    "line": 57,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Fideloper\\Proxy\\TrustProxies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\dingo\\api\\src\\Http\\Middleware\\Request.php",
                    "line": 111,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Dingo\\Api\\Http\\Middleware\\Request::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 105,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 145,
                    "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 110,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::sendRequestThroughRouter(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 470,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::handle(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 442,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::call('POST', 'https://object.local/graphql', array(0), array(0), array(1), array(3), '{\"query\":\"\\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\\n                onlineStoreThemeAssetCreate(input: $input) {\\n                    themeAsset {\\n                        themeId\\n                        key\\n                        content\\n                    }\\n                }\\n            }\\n        \",\"variables\":{\"input\":{\"themeId\":\"vogue\",\"content\":\"string\",\"key\":\"js\\/20220307.9.js\"}}}')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 301,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::json('POST', 'https://object.local/graphql', array(2), array(3))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Testing\\MakesGraphQLRequests.php",
                    "line": 79,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::postJson('https://object.local/graphql', array(2), array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Testing\\MakesGraphQLRequests.php",
                    "line": 62,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::postGraphQL(array(2), array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest.php",
                    "line": 36,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::graphQL('\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\n                onlineStoreThemeAssetCreate(input: $input) {\n                    themeAsset {\n                        themeId\n                        key\n                        content\n                    }\n                }\n            }\n        ', array(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 1154,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 842,
                    "call": "PHPUnit\\Framework\\TestCase::runTest()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestResult.php",
                    "line": 693,
                    "call": "PHPUnit\\Framework\\TestCase::runBare()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 796,
                    "call": "PHPUnit\\Framework\\TestResult::run(instance of Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest(1))"
                },
                {
                    "file": "C:\\Users\\Lenovo\\AppData\\Local\\Temp\\PHP57E0.tmp",
                    "line": 261,
                    "call": "PHPUnit\\Framework\\TestCase::run(instance of PHPUnit\\Framework\\TestResult(1))"
                },
                {
                    "file": "C:\\Users\\Lenovo\\AppData\\Local\\Temp\\PHP57E0.tmp",
                    "line": 1040,
                    "function": "__phpunit_run_isolated_test()"
                }
            ]
        }
    ],
    "data": {
        "onlineStoreThemeAssetCreate": null
    }
}].


Failed asserting that an array has the subset Array &0 (
    'data' => Array &1 (
        'onlineStoreThemeAssetCreate' => Array &2 (
            'themeAsset' => Array &3 (
                'themeId' => 'vogue'
                'content' => 'string'
                'key' => 'js/20220307.9.js'
            )
        )
    )
).
--- Expected
+++ Actual
@@ @@
   ),
   'data' =>
   array (
-    'onlineStoreThemeAssetCreate' =>
-    array (
-      'themeAsset' =>
-      array (
-        'themeId' => 'vogue',
-        'content' => 'string',
-        'key' => 'js/20220307.9.js',
-      ),
-    ),
+    'onlineStoreThemeAssetCreate' => NULL,
   ),
 )

E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Assert.php:108
E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:479
E:\wwwroot\object\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php:43

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
PS E:\wwwroot\object>



7、虽然现在针对测试用例配置了一个单独的测试数据库,无需要担心影响到开发数据库。但是要避免多次测试时,测试不通过的问题,决定尽量避免掉请求参数的重复。将变量 key 的值基于时间戳生成。其值示例:js/20220307144656.js。这样可以保证在每一秒钟可以运行一次测试,且能够通过。后续可以在此测试文件中添加一个测试删除此条创建的记录的方法。如图3

虽然现在针对测试用例配置了一个单独的测试数据库,无需要担心影响到开发数据库。但是要避免多次测试时,测试不通过的问题,决定尽量避免掉请求参数的重复。将变量 key 的值基于时间戳生成。其值示例:js/20220307144656.js。这样可以保证在每一秒钟可以运行一次测试,且能够通过。后续可以在此测试文件中添加一个测试删除此条创建的记录的方法。

图3


        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/' . date('YmdHis') . '.js',
        ];


 

]]>
https://www.shuijingwanwq.com/2022/04/01/6233/feed/ 0
基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),在 api 的 tests 目录中准备用户相关操作的一些自动化测试的样例(API 测试),确保应用程序在改变或增加新的功能时不会影响现有的功能 https://www.shuijingwanwq.com/2018/04/13/2597/ https://www.shuijingwanwq.com/2018/04/13/2597/#respond Fri, 13 Apr 2018 08:16:17 +0000 http://www.shuijingwanwq.com/?p=2597 浏览量: 1,263

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
+ &lt;p class="help-block help-block-error">Name不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Email不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Subject不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Body不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">验证码不正确。&lt;/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
+ &lt;p class="help-block help-block-error">&lt;/p>
+ &lt;p class="help-block help-block-error">Email不是有效的邮箱地址。&lt;/p>
+ &lt;p class="help-block help-block-error">&lt;/p>
+ &lt;p class="help-block help-block-error">&lt;/p>
+ &lt;p class="help-block help-block-error">&lt;/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
+ &lt;p class="help-block help-block-error">Username不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Password不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">&lt;/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
+ &lt;p class="help-block help-block-error">Username不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Email不能为空。&lt;/p>
+ &lt;p class="help-block help-block-error">Password不能为空。&lt;/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
+ &lt;p class="help-block help-block-error">&lt;/p>
+ &lt;p class="help-block help-block-error">Email不是有效的邮箱地址。&lt;/p>
+ &lt;p class="help-block help-block-error">&lt;/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


]]>
https://www.shuijingwanwq.com/2018/04/13/2597/feed/ 0