在 Yii 2 中使用 CodeCeption

安装 Yii2

使用 Composer 安装 Yii 2 高级模板项目,Yii 安装文档

1
2
3
4
5
6
7
8
# 安装 Yii
composer global require "fxp/composer-asset-plugin:^1.2.0"
composer create-project --prefer-dist yiisoft/yii2-app-advanced yii-application
# Yii 初始化
cd yii-application
composer install
yii init

配置 Apache VirtualHost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<VirtualHost *:80>
ServerName frontend.dev
DocumentRoot "/path/to/yii-application/frontend/web/"
<Directory "/path/to/yii-application/frontend/web/">
# use mod_rewrite for pretty URL support
RewriteEngine on
# If a directory or a file exists, use the request directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Otherwise forward the request to index.php
RewriteRule . index.php
# use index.php as index file
DirectoryIndex index.php
# ...other settings...
# Apache 2.4
Require all granted
## Apache 2.2
# Order allow,deny
# Allow from all
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName backend.dev
DocumentRoot "/path/to/yii-application/backend/web/"
<Directory "/path/to/yii-application/backend/web/">
# use mod_rewrite for pretty URL support
RewriteEngine on
# If a directory or a file exists, use the request directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Otherwise forward the request to index.php
RewriteRule . index.php
# use index.php as index file
DirectoryIndex index.php
# ...other settings...
# Apache 2.4
Require all granted
## Apache 2.2
# Order allow,deny
# Allow from all
</Directory>
</VirtualHost>

安装 Codeception

如果在项目的 vendor 目录看到 codeception 目录,代表 Yii 集成了 codeception package,不需要单独安装 codeception。

执行以下命令安装 Codeception package

1
2
3
4
5
6
7
# Codeception
sudo composer global require "codeception/codeception=*"
sudo composer global require "codeception/specify=*"
sudo composer global require "codeception/verify=*"
# Facker 用于虚拟数据生成
sudo composer require --dev "yiisoft/yii2-faker=*"

将 codeception 命令配置进环境变量中

1
2
3
4
5
6
7
8
vim .zshrc
# 如果在 Composer 安装 Codeception,alias 路径如下
alias codecept="/Users/用户名称/.composer/vendor/codeception/codeception/codecept"
# 如果在项目根目录下安装 Codeception,alias 路径如下
alias codecept="/Users/用户名称/项目路径/vendor/codeception/base/codecept"
source .zshrc

在项目中使用 codeception

1
2
3
4
cd frontend/
codecept bootstrap
codecept build
codecept run

单元测试(Unit Test)

单元测试是针对程序模块来进行正确性检验的测试工作,可以通过单元测试来确认深藏在代码里面的某些功能仍然可用, 单元测试能消除程序单元的不可靠性。

Codeception 的单元测试功能是基于 PHPUnit 之上的, 你可以照样写 PHPUnit 的测试代码, Codeception 一样能运行。PHPUnit 官方文档

创建单元测试

使用 Codeception 的生成器来执行以下命令生成单元测试

1
2
cd frontend
codecept generate:test unit SimpleExample

上述命令会在 tests/unit 目录创建一个 ExampleTest 文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
class SimpleExampleTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
protected $stack;
protected function _before()
{
$this->stack = [];
}
protected function _after()
{
}
// tests array push and array pop
public function testArrayPushAndArrayPop()
{
$this->stack = [];
$this->assertEquals(0, count($this->stack));
array_push($this->stack, 'foo');
$this->assertEquals('foo', $this->stack[count($this->stack) - 1]);
$this->assertEquals(1, count($this->stack));
$value = array_pop($this->stack);
$this->assertEquals('foo', $value);
$this->assertEquals(0, count($this->stack));
}
}

测试依赖

通过 @depends 让测试方法之间进行依赖关联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php
class DependsExampleTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
public function testEmpty()
{
$stack = [];
$this->assertEquals(0, count($stack));
return $stack;
}
/**
* @param $array
*
* @return array
*
* @depends testEmpty
*/
public function testPush(array $array)
{
array_push($array, 'LuisEdward');
$this->assertEquals('LuisEdward', $array[count($array) - 1]);
$this->assertNotEquals('Leon', $array[count($array) - 1]);
$this->assertEquals(1, count($array));
return $array;
}
/**
* @param $arrayAliasNames
*
* @return array
*
* @depends testPush
*/
public function testPop(array $arrayAliasNames)
{
$this->assertEquals('LuisEdward', array_pop($arrayAliasNames));
$this->assertEmpty($arrayAliasNames);
}
protected function _before()
{
}
protected function _after()
{
}
}
抛出异常

通过 @expectedException 或方法 $this->expectException() 在测试实例中抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace frontend\tests;
class ExceptionTest extends \Codeception\Test\Unit
{
public function testException()
{
$this->expectException(InvalidArgumentException::class);
}
/**
* @expectedException InvalidArgumentException
*/
public function testExceptionTow()
{
}
}
断点输出

通过方法 $this->expectOutputString() 生成一个预期的输出。当实际输出和预期输出不一致时,则测试不通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
use PHPUnit\Framework\TestCase;
class OutputTest extends TestCase
{
public function testExpectFooActualFoo()
{
$this->expectOutputString('foo');
print 'foo';
}
public function testExpectBarActualBaz()
{
$this->expectOutputString('bar');
print 'baz';
}
}

Codeception 的更多 Unit Tests 信息请参考链接:

创建接口测试(RESTFUL API TEST)

创建 RESTFUL API 测试配置文件,新增文件 api.suite.yml 和文件夹 api

1
codecept generate:suite api

修改配置文件 api.suite.yml 如下:

1
2
3
4
5
6
7
actor: ApiTester
modules:
enabled:
- REST:
url: http://workerbee.app/v1/
depends: PhpBrowser
part: Json

创建 RESTFUL API 测试文件

1
codecept generate:cept api UserSignIn

修改生成文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$I = new ApiTester($scenario);
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
$I->haveHttpHeader('Accept', 'application/json');
$I->sendPOST('user/sign-in', ['username' => 'admin', 'pwd' => '123456']);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK);
$I->seeResponseMatchesJsonType([
"id" => 'integer',
"username" => "string",
"password_hash" => "string",
"access_token" => "string",
"email" => "string:email",
"status" => 'integer',
"role" => "string",
"created" => 'integer',
"updated" => 'integer',
"mobile" => 'string||null||integer',
]);

常用的 REST API 如下:

  • wantTo():配置测试标题
  • sendGET():发送 HTTP GET 请求
  • sendPUT():发送 HTTP PUT 请求
  • sendPOST():发送 HTTP POST 请求
  • sendDELETE():发送 HTTP DELETE 请求
  • haveHttpHeader():设置 HTTP 请求头
  • seeHttpHeader():检查 HTTP 请求头
  • seeResponseCodeIs():检查响应状态码
  • seeResponseContains():检查响应内容
  • seeResponseContainsJson():检查响应数据类型是否为 JSON 和核对响应内容
  • seeResponseIsJson():检查响应是否返回 JSON 数据
  • seeResponseJsonMatchesJsonPath():使用 JSON 数据去检查响应内容
  • seeResponseMatchesJsonType():检查响应数据的数据类型

Codeception 的更多 RESTFUL API 信息请参考链接:

数据库测试(DB TEST)

常用的 DB TEST API 如下:

  • seeInDatabase()
  • dontSeeInDatabase()
  • haveInDatabase()
  • grabFromDatabase()

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
// 检查 User 表是否存在 'username' 为 'Davert', 'email' 为 'davert@mail.com' 的数据,存在则报错
$I->dontSeeInDatabase('user', ['username' => 'Davert', 'email' => 'davert@mail.com']);
// 检查 User 表是否存在 'username' 为 'okirlin', 'email' 为 'brady.renner@rutherford.com' 的数据,不存在则报错
$I->seeInDatabase('user', ['username' => 'okirlin', 'email' => 'brady.renner@rutherford.com']);
// 检查 User 表是否能够新增以下数据,无法新增则报错
$I->haveInDatabase('user',
[
'username' => 'miles',
'email' => 'miles@davis.com',
'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS321',
'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2',
'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_1487317115',
'email' => '506510463@qq.com',
'role' => 10,
'status' => 0,
'created_at' => 1391885313,
'updated_at' => 1391885313,
]);
// 按照正序获取一条 username = miles 的数据的 email 字段值
$mail = $I->grabFromDatabase('user', 'email', array('username' => 'troy.becker'));
var_dump($mail);exit;

Codeception 的更多 DB TEST API 信息请参考链接: