Codeception快速开始测试
支持的测试类型
Codeception 支持三种测试类型,单元、功能和验收测试。
| 测试类型 | 单元测试( Unit Tests) | 功能测试(Functional Tests) | 验收测试(Acceptance Tests) |
|---|---|---|---|
| 测试范围 | 单个 PHP 类 | PHP 框架(路由、控制器等) | 浏览器中的页面(Chrome、Firefox 或 ) |
| 测试计算机需要访问项目的 PHP 文件 | 是的 | 是的 | 不 |
| 需要网络服务器 | 不 | 不 | 是的 |
| 脚本 | 不 | 不 | 是的 |
| 需要其他软件 | 没有 | 没有 | 用于浏览器测试的Selenium |
| 速度 | 快 | 快 | 慢 |
| 配置文件 | unit.suite.yml |
functional.suite.yml |
acceptance.suite.yml |
| 优点 | 1.最快的测试; 2.能把测试覆盖到特别刁钻的程序逻辑上, 这是 functional 或者 acceptance 所做不到的; 3.允许你测试最核心代码, 确定核心代码的健壮性; 4.写单元测试的程序要都是好程序员 。 | 1.跟 Acceptance tests 类似, 但是少了打开浏览器来渲染, 速度快多了; 2.能提供更详细的分析, 如数据库或者 email; 3.可读性很强, 虽然没法让测试人员看到打开浏览器模拟人工测试, 但是还是可以让别人信服; 4.比较稳定, 只有当大规模的代码变更, 或者把代码从一个框架转移到另一个框架的时候, 才会有影响. | 1.可用来测试任何网站; 2.完全基于浏览器, 可以测试 Javascript 甚至是 ajax 请求; 3.可以把运行状态给 产品经理 或者 客户看, 让人信服; 4.不需要多余的配置, 对 App 源码修改要求最少, 代码适应性好, 可以当成整个应用来测试, 不在乎内部实现. |
| 缺点 | 1.因为是单元测试, 会把代码分为多个小单元单独测试, 但是各个单元之间的对接测试不到; 2.对代码的修改非常敏感, 很多项目的 test 最后没用上就是因为测试跟不上业务逻辑代码的修改. | 1.无法测试 javascript 和 ajax; 2.因为使用代码相对简单的模拟一个浏览器请求, 测试的可行度, 或者说完整性, 会相对较差; 3.需要一个框架的支持; | 1.测试速度缓慢, 因为需要运行在浏览器和真实的数据库上; 2.相比单元测试, 做不到完全的测试, 有些细微的逻辑可能会错过; 3.在运行的时候有时候会发生不可控的事情, 因为浏览器的渲染, javascript 的运行, 有时候会有意想不到的情况发生. 4.再一次强调, 此测试会非常慢; |
安装
单个项目:
composer require "codeception/codeception" --dev
如果想全局使用,可以直接下载可执行文件,然后把文件放到php执行文件所在的目录。
wget https://codeception.com/codecept.phar
快速全局使用:
如果是windows系统,可以把下面代码放入codecept.bat,然后把这个文件放到codecept.phar同级。
@php "%~dp0codecept.phar" %*
Mac和Linux更简单了,直接设置一个别名即可:
alias codecept='php vendor/bin/codecept'
然后就可以愉快的全局使用了。
PS D:WWWlaravel-test> codecept
Codeception 4.1.31
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-c, --config[=CONFIG] Use custom path for config
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
build Generates base classes for all suites
clean Recursively cleans log and generated code
console Launches interactive test console
dry-run Prints step-by-step scenario-driven test or a feature
help Display help for a command
init Creates test suites by a template
list List commands
run Runs the test suites
self-update [selfupdate] Upgrade D:laragonbinphpphp-7.4.28-Win32-vc15-x64codecept.phar to the latest version
config
config:validate Validates and prints config to screen
generate
generate:cept Generates empty Cept file in suite
generate:cest Generates empty Cest file in suite
generate:environment Generates empty environment config
generate:feature Generates empty feature file in suite
generate:groupobject Generates Group subscriber
generate:helper Generates new helper
generate:pageobject Generates empty PageObject class
generate:scenarios Generates text representation for all scenarios
generate:snapshot Generates empty Snapshot class
generate:stepobject Generates empty StepObject class
generate:suite Generates new test suite
generate:test Generates empty unit test file in suite
gherkin
gherkin:snippets Fetches empty steps from feature files of suite and prints code snippets for them
gherkin:steps Prints all defined feature steps
初始化
codecept bootstrap
生成的目录文件结构如下:
D:WWWLaravel-testtests ├─acceptance ├─Feature ├─functional ├─Unit ├─_data ├─_output └─_support ├─Helper └─_generated
测试
验收测试
我们先设想这样一个场景:当技术人员开发完毕, 其客户, 产品经理, 或者是测试人员, 他们怎么确定产品的可用性? 一般情况下, 他们都是执行以下几个步骤进行测试:
-
打开浏览器;
-
输入 url;
-
看到一些信息, 并确定了这个页面是可用的;
-
点击某个 url;
-
填写表单, 并提交表单, 看到了某些信息, 并确定此功能是可用的。
这种测试方式我们称之为 手动测试, 或 人工测试, 与其相反的是 自动化测试, Codeception 的 Acceptance Tests 会利用浏览器的编程接口, 做到以上的 人工测试 涉及到的步骤完全自动化, 大大节省了人工成本.
生成一个验收测试:
D:WWWlaravel-test>codecept generate:cest acceptance First Test was created in D:WWWlaravel-testtestsacceptanceFirstCest.php
具体代码
<?php
class FirstCest
{
public function _before(AcceptanceTester $I)
{
echo 'Test Begin:';
}
// tests
public function tryToTest(AcceptanceTester $I)
{
$I->amOnPage('/index/about');
$I->see('我的介绍');
}
}
运行测试
D:WWWlaravel-test>codecept run --steps
可以看到验收测试通过。现在我们把文本从“我的介绍”改成“我的11介绍”,理论上这时候测试应该通过不了,看看测试出错是啥情况。果然,报告验收错误。
功能测试
功能测试模拟一个 web 请求 (模拟 $_GET 和 $_POST 等变量), 发送给 App, 应用返回 HTML 结果, 在测试的过程中, 可以分析并进行 assert 判定返回的数据, 甚至可以检查数据是否正常的存储到数据库.
函数测试需要有一个测试环境, 几个有名的框架, 像 Laravel 就有现成的 Package 可以用来集成.
共享内存
在功能测试中,与传统方式运行应用程序不同,PHP 应用程序在处理完请求后不会停止。由于所有请求都在一个内存容器中运行,因此它们不是隔离的。因此,如果您发现您的测试在不应该失败时神秘地失败 – 请尝试执行单个测试。这将显示测试是否失败,因为它们在运行期间未被隔离。保持内存清洁,避免内存泄漏,并清理全局和静态变量。
配置
codecept generate:test functional First
配置框架支持特性,例如Laravel的支持:
# D:WWWlaravel-testtestsfunctional.suite.yml actor: FunctionalTester modules: enabled: - Laravel5 # add a framework module here - HelperFunctional step_decorators: ~
还需要安装一些拓展:
codecept init upgrade4
具体代码
public function tryLogin(FunctionalTester $I)
{
$I->amOnPage('/');
$I->click('Sign Up');
$I->submitForm('#signup', array('username' => 'MilesDavis', 'email' => 'miles@davis.com'));
$I->see('Thank you for Signing Up!');
$I->seeEmailSent('miles@davis.com', 'Thank you for registration');
$I->seeInDatabase('users', array('email' => 'miles@davis.com')); # 不是所有框架都有这个方法
}
单元与集成测试
单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作, 当 functional 或者 acceptance 测试都检查不到 最小单位 的逻辑时, 还能通过 单元测试 确认深藏在代码里面的某些功能仍然可用, 单元测试能消除程序单元的不可靠性.
Codeception 的单元测试功能是基于 PHPUnit 之上的, 你可以照样写 PHPUnit 的测试代码, Codeception 一样能运行.
Codeception 在 PHPUnit 的基础上提供了一系列工具能让单元测试更加简单, 代码可读性更高. 单元测试是最复杂最繁琐的测试, 并且是会跟着业务逻辑代码的改变而改变, 在实际开发中技术人员会经常因为需求、业务的变更而修改单元测试, 提高其可读性和易用性可以帮助相关人员更加快速的跟上一切变化.
codecept generate:test unit First
如果要测试数据库模块,需要先安装Db模块,并配置。
composer require codeception/module-db --dev
还要进行配置:
# D:WWWlaravel-testcodeception.yml ... params: - .env # load params from environment vars modules: config: Db: dsn: "mysql:host=%DB_HOST%;dbname=%DB_DATABASE%" user: "%DB_USERNAME%" password: "%DB_PASSWORD%" populate: false cleanup: false reconnect: true
具体代码:
public function testUpdateUser()
{
$id = $this->tester->haveRecord('users', ['name' => 'YSP Conn AAA', 'email' => 'bill@qq.com', 'password' => 'sadfasdd',]);
// access model
$user = User::find($id);
$user->name = 'bill';
$user->save();
// verify data was saved using framework methods
$this->tester->seeRecord('users', ['name' => 'bill']);
$this->tester->dontSeeRecord('users', ['name' => 'YSP Conn AAA']);
}
运行测试:
codecept run unit --steps
接口测试
生成配置:
codecept generate:suite api
REST API
注意:REST API 测试需要安装codeception/module-rest包。
REST Web 服务通过 HTTP 使用标准方法访问:GET、POST、PUT、DELETE 。它们允许用户从服务接收和操作实体。访问 Web 服务需要 HTTP 客户端,因此要使用它,您需要设置PhpBrowser模块或框架模块之一。例如,我们可以将Symfony模块用于 Symfony2 应用程序,以便在内部忽略 Web 服务器并测试 Web 服务。
在 api.suite.yml中配置模块:
REST 模块将根据此配置进行连接PhpBrowser。根据 Web 服务的不同,我们可能会处理 XML 或 JSON 响应。Codeception可以很好地处理这两种数据格式,但是如果您不需要其中一种格式,则可以显式指定将使用模块的JSON或XML部分:
actor: ApiTester modules: enabled: - REST: url: http://serviceapp/api/v1/ depends: PhpBrowser part: Json
API测试可以是功能性的,可以使用Symfony,Laravel,Zend或任何其他框架模块执行。您将需要稍微更新一下配置:
actor: ApiTester modules: enabled: - REST: url: /api/v1/ depends: Laravel5
配置新的测试套件后,我们可以创建第一个示例测试:
codecept generate:cest api CreateUser
它将被称为CreateUserCest.php 。我们需要为每个测试实现一个公共方法。让我们通过 REST API 测试用户的创建。
<?php
class CreateUserCest
{
// tests
public function createUserViaAPI(ApiTester $I)
{
$I->amHttpAuthenticated('service_user', '123456');
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
$I->sendPost('/users', [
'name' => 'davert',
'email' => 'davert@codeception.com'
]);
$I->seeResponseCodeIsSuccessful();
$I->seeResponseIsJson();
$I->seeResponseContains('{"result":"ok"}');
}
}
授权
若要授权对外部资源的请求,提供程序通常要求您使用标头进行授权。可以使用haveHttpHeader命令在请求之前设置其他标头:
<?php
$I->haveHttpHeader('api_key', 'special-key');
对于常见的授权模式,请使用下列方法之一:
-
amAWSAuthenticated -
amBearerAuthenticated -
amDigestAuthenticated -
amHttpAuthenticated -
amNTLMAuthenticated
发送请求
测试中的实际操作仅在发送请求时发生。在请求之前,您可以提供其他 http 标头,这些标头将在下一个请求中用于设置授权或预期的内容格式。
<?php
$I->haveHttpHeader('accept', 'application/json');
$I->haveHttpHeader('content-type', 'application/json');
设置标头后,您可以发送请求。使用sendGet设置数据:
<?php
// pass in query params in second argument
$response = $I->sendGet('/posts', [ 'status' => 'pending' ]);
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
要创建或更新数据,您可以使用其他常用方法:
-
sendPost -
sendPut -
sendPatch -
sendDelete -
sendPatch
使用自定义send方法发送请求:
<?php
$response = $I->send('TRACE', '/posts');
sendAsJson 方法在模块 rest 1.4.1 中引入
如果 API 终端节点接受 JSON,则可以使用带AsJson后缀的send方法自动转换数据。在这种情况下,会发送Content-Type:application/json的标头和响应:
$I->sendPostAsJson('/users', ['name' => 'old name']);
$users = $I->sendGetAsJson('/users');
$I->sendPutAsJson('/users/' . $users[0]['id'], ['name' => 'new name']);
$I->sendDeleteAsJson('/users/' . $users[1]['id']);
要启用带有AsJson后缀的步骤,请在套件配置中启用CodeceptionStepAsJson步骤装饰器:
actor: ApiTester step_decorators: - CodeceptionStepAsJson
重建操作:
codecept build
sendGetAsJson、sendPutAsJson和其他部分作为步骤装饰器实现。
JSON 结构验证
如果我们期望收到JSON响应,我们可以使用检查其结构。它的外观和声音都像XPath,但旨在处理JSON数据,但是我们可以将JSON转换为XML并使用XPath来验证结构。这两种方法都是有效的,可以在 REST 模块中使用:
<?php
$I->sendGet('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseJsonMatchesJsonPath('$[0].user.login');
$I->seeResponseJsonMatchesXpath('//user/login');
如果需要验证响应中的字段类型,则可以应用更详细的检查。您可以通过使用 操作来执行此操作,在该操作中定义 JSON 响应的结构。
<?php
$I->sendGet('/users/1');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseMatchesJsonType([
'id' => 'integer',
'name' => 'string',
'email' => 'string:email',
'homepage' => 'string:url|null',
'created_at' => 'string:date',
'is_active' => 'boolean'
]);
Codeception使用这种简单而轻量级的定义格式,。
使用响应
响应从send*方法返回:
<?php
$users = $I->sendGet('/users');
// alternatively
$users = $I->grabResponse();
当您需要从响应中获取值并在下一个请求中使用它时,可以使用grab*方法。例如grabDataFromResponseByJsonPath,使用允许查询 JSON 以获取值。
<?php
list($id) = $I->grabDataFromResponseByJsonPath('$.id');
$I->sendGet('/pet/' . $id);
验证数据 JSON 响应
上一示例的最后一行验证了响应是否包含提供的字符串。但是,我们不应该依赖它,因为根据内容格式,我们可以使用相同的数据接收不同的结果。我们实际需要的是检查响应是否可以解析,并且它包含我们期望的一些值。在JSON的情况下,我们可以使用seeResponseContainsJson方法。
<?php
// matches {"result":"ok"}'
$I->seeResponseContainsJson(['result' => 'ok']);
// it can match tree-like structures as well
$I->seeResponseContainsJson([
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
]);
您可能希望对响应执行更复杂的断言。这可以通过在 类中编写自己的方法来完成。要访问最新的 JSON 响应,您需要获取REST模块的response属性。让我们用seeResponseIsHtml方法进行演示:
<?php
namespace Helper;
class Api extends CodeceptionModule
{
public function seeResponseIsHtml()
{
$response = $this->getModule('REST')->response;
$this->assertRegExp('~^<!DOCTYPE HTML(.*?)<html>.*?</html>~m', $response);
}
}
测试文件类型
Codeception支持3种测试格式,之前讲过的基于场景的Cept格式,Codeception也可以执行PHPUnit 单元测试测试文件和Cest格式。
Codeception的cest和cept有什么区别:
他们的格式是唯一的区别: Cept是基于场景的格式, Cest是基于类的格式.
Cest是OOP设计结合了场景驱动测试,如果你想组合一些测试场景到一个中你应该考虑使用Cest格式,下面的例子我们用一个单独的文件测试CRUD操作,但其中有几个测试,每次操作一个。
cest例子:
<?php class FirstCest { public function _before(AcceptanceTester $I) { } // tests public function tryToTest(AcceptanceTester $I) { $I->wantTo('log in as regular user'); $I->amOnPage('/login'); $I->fillField('Username','john'); $I->fillField('Password','secret'); $I->click('Login'); $I->see('Hello john'); } }
cept例子:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('log in as regular user'); $I->amOnPage('/login'); $I->fillField('Username','john'); $I->fillField('Password','secret'); $I->click('Login'); $I->see('Hello john');
如果驱动版本不对,需要下载对应的工具:
http://chromedriver.storage.googleapis.com/index.html
https://registry.npmmirror.com/binary.html?path=chromedriver/
错误报告
默认情况下,Codeception 使用E_ALL & ~E_STRICT & ~E_DEPRECATED错误报告级别。在功能测试中,您可能希望根据框架的错误策略更改此级别。可以在套件配置文件中设置错误报告级别:
actor: FunctionalTester ... error_level: E_ALL & ~E_STRICT & ~E_DEPRECATED
error_level也可以在codeception.yml文件中全局设置。为此,您需要指定error_level为settings 的一部分。有关详细信息,请参阅。请注意,特定于套件的error_level值将覆盖全局值。
运行测试
通过运行 run 命令开始运行测试:
codecept run
通过第一个参数可以运行一个套件。
codecept run acceptance
指定第二个参数可以运行一个测试,从套件目录指定一个本来路径。
codecept run acceptance SigninCept.php
另外你还可以提供一个完整的测试文件路径:
codecept run tests/acceptance/SigninCept.php
你可以从一个测试类(Cest or 单元测试格式)里执行一个测试:
codecept run tests/acceptance/SignInCest.php:anonymousLogin codecept run api ExportCest.php:ajaxTask --html
你还可以提供一个目录路径:
codecept run tests/acceptance/backend
上面命令是执行 tests/acceptance/backend 目录里的所有测试。
要执行一组存储不在同目录的测试时,你可以用groups 组织它们。
我们获取详细的输出:
codecept run acceptance --steps codecept run api -v
测试报告:
你可以通过 –xml 选项生成JUnit XML,通过–html输出HTML报告。
codecept run api --steps --xml --html myTest.html # 自定义html报告文件名 codecept run api SpiderReportV2Cest.php:stat --html test.html # 指定运行某个测试文件里面的某个方法
上面这个命令将执行所有套件的测试,逐步显示,并且构建将HTML和XML报告存储到tests/_output/目录。
了解所有可用的选项运行这个命令:
php codecept.phar help run
使用模块
例如想使用db模块功能,需要获取相应driver
DB相关操作参考文档:https://codeception.com/docs/modules/Db
# 使用redis使用session
public function getLoginSession()
{
// 这里只需要设置redis的session值即可,程序dev也从redis获取session值,文件的时候可能是docker与windows之间有些不兼容的地方还是怎么的
// 使用文件作为session驱动的时候不能识别session信息
$key = 'wsoc' . Api::SESSION_ID;
$redis = $this->getModule('Redis');
$session = file_get_contents(__DIR__ . '/../../' . Api::SESSION_ID . '.txt'); //设置session id
$redis->haveInRedis('string', $key, $session);
}