codeception单元测试语法介绍
上篇我们介绍了 “PhpStorm集成codeception单元测试”( https://www.cnblogs.com/phplog/articles/12058369.html )
这篇我们重点介绍 codeception的语法以及响应单元测试命令:
入门指南
让我们来看看Codeception的架构,我们假定你已经安装好了codeception,并且用 codecept bootstrap 初始化了你第一个测试套件,Codeception 将会生成unit, functional, 和 acceptance三个部分,在你的 /test 文件夹你会看到有三个配置文件和三个名称与之对应的目录。测试套件是一组有一个共同目的的测试组合。
Actors
Actiors是Codeception的一个重要概念,用来表示测试一个人行为。我们有一个UnitTester来执行功能测试代码;我们还有一个FunctionalTester,负责质量测试,采用内部的知识来把应用程序当作一个整体来测试;还有一个AcceptanceTester,以用户方式通过我们提供的界面检查应用程序的工作。
Actor类不需要写,而是通过套件生成。 Actor类的方法都是通过Codeception模块生成的,每个模块为不同的测试目的提供预定义的动作,并且它们可以被组合以适应测试环境,Codeception尝试通过这些模块解决可能存在的90%的问题,所以你不必另起炉灶。我们认为你可以花更多的时间写测试和支撑代码以使得这些测试得以运行。默认情况下AcceptanceTester依赖于PhpBrowser模块,它们在tests/acceptance.suite.yml
配置文件中:
actor: AcceptanceTester modules: enabled: - PhpBrowser: url: http://www.thinkapp.com - \Helper\Acceptance step_decorators: ~
在这个配置文件里你可以根据你的需求启用/禁用重新配置模块。 你改变配置的时候actor类会自动创建,如果Actor类没有如你期望创建或更新,那么你可以手动执行 build
命令生成它们:
$ codecept build
Bootstrap
每一个套件都有他们自己的bootstrap文件,命名为 _bootstrap.php
,测试套件执行前它会执行。 tests
目录下的是全局bootstrap文件,它可以用来include附加文件。
写一个场景例子
默认测试会被写成叙述式的场景,创建的场景的PHP文件必须以Cept
后缀命名。
现在我们创建一个文件 tests/acceptance/SigninCept.php
我们可以通过一下命令:
$ codecept generate:cept acceptance Signin
一个场景总是从Actor类初始化开始,接着,在场景中输入类似以 $I->
接着从自动完成列表里选择一个合适的行为。
<?php $I = new AcceptanceTester($scenario); ?>
将设我们有一个'login'页面,让我们登录到我们的页面通过用户名密码进行身份验证,然后我们到一个有Hello, %username%
这样文本的页面,让我们来看看在Codeception里怎么写这个场景。
<?php $I = new AcceptanceTester($scenario); $I->wantTo('log in as regular user'); $I->amOnPage('/login'); $I->fillField('Username','davert'); $I->fillField('Password','qwerty'); $I->click('Login'); $I->see('Hello, davert'); ?>
在执行这个测试之前,我们应该确保网站正在本地服务器上运行着。我们打开tests/acceptance.suite.yml
配置文件将URL参数替换为你的web应用程序的访问URL:
actor: AcceptanceTester modules: enabled: - PhpBrowser: url: http://www.thinkapp.com - \Helper\Acceptance
配置好URL后用 run
命令运行测试:
$ codecept run
这里我们会看到类似的输出。
让我们获取详细的输出:
$ codecept run acceptance --steps
我们应该看到一步一步的行为报告。
Acceptance Tests (1) ------------------------------- Trying to log in as regular user (SigninCept.php) Scenario: * I am on page "/login" * I fill field "Username" "davert" * I fill field "Password" "qwerty" * I click "Login" * I see "Hello, davert" OK ---------------------------------------------------- Time: 0 seconds, Memory: 21.00Mb OK (1 test, 1 assertions)
这个简单的测试可以扩展到一个完整的场景使用场景。 所以,通过模拟用户的行为,您可以测试您的任何网站。 接下来看一个例子。
Codeception 简介
Codeception 简单来说, 分为以下几种测试
- Acceptance Tests 验收测试
- Functional Tests 功能测试
- Unit Tests 单元测试
接下来我们来一一介绍每一种测试的优缺点.
Acceptance Tests 验收测试
我们先设想这样一个场景:当技术人员开发完毕, 其客户, 产品经理, 或者是测试人员, 他们怎么确定产品的可用性? 一般情况下, 他们都是执行以下几个步骤进行测试:
- 打开浏览器;
- 输入 url;
- 看到一些信息, 并确定了这个页面是可用的;
- 点击某个 url;
- 填写表单, 并提交表单, 看到了某些信息, 并确定此功能是可用的
这种测试方式我们称之为 手动测试
, 或 人工测试
, 与其相反的是 自动化测试
, Codeception 的 Acceptance Tests
会利用浏览器的编程接口, 做到以上的 人工测试
涉及到的步骤完全自动化, 大大节省了人工成本.
先贴代码
<?php
$I = new AcceptanceTester($scenario);
$I->amOnPage('/');
$I->click('Sign Up');
$I->submitForm('#signup', array('username' => 'MilesDavis', 'email' => 'miles@davis.com'));
$I->see('Thank you for Signing Up!');
优点
- 可用来测试任何网站;
- 完全基于浏览器, 可以测试
Javascript
甚至是 ajax 请求; - 可以把运行状态给 产品经理 或者 客户看, 让人信服;
- 不需要多余的配置, 对 App 源码修改要求最少, 代码适应性好, 可以当成整个应用来测试, 不在乎内部实现.
缺点
- 测试速度缓慢, 因为需要运行在浏览器和真实的数据库上;
- 相比单元测试, 做不到完全的测试, 有些细微的逻辑可能会错过;
- 在运行的时候有时候会发生不可控的事情, 因为浏览器的渲染,
javascript
的运行, 有时候会有意想不到的情况发生. - 再一次强调, 此测试会非常慢;
Functional Tests 功能测试
功能测试模拟一个 web 请求 (模拟 $_GET 和 $_POST 等变量), 发送给 App, 应用返回 HTML 结果, 在测试的过程中, 可以分析并进行 assert
判定返回的数据, 甚至可以检查数据是否正常的存储到数据库.
函数测试需要有一个测试环境, 几个有名的框架, 像 Laravel
就有现成的 Package
可以用来集成.
以下是一个简单的功能测试:
<?php
$I = new FunctionalTester($scenario);
$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'));
可以看出跟 Acceptance Tests 验收测试
语法类似, 因为集成了测试环境, 允许检查 email 和 数据库.
优点
- 跟
Acceptance tests
类似, 但是少了打开浏览器来渲染, 速度快多了; - 能提供更详细的分析, 如数据库或者 email;
- 可读性很强, 虽然没法让测试人员看到打开浏览器模拟人工测试, 但是还是可以让别人信服;
- 比较稳定, 只有当大规模的代码变更, 或者把代码从一个框架转移到另一个框架的时候, 才会有影响.
缺点
- 无法测试 javascript 和 ajax;
- 因为使用代码相对简单的模拟一个浏览器请求, 测试的可行度, 或者说完整性, 会相对较差;
- 需要一个框架的支持;
Unit Tests 单元测试
单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作, 当 functional
或者 acceptance
测试都检查不到 最小单位
的逻辑时, 还能通过 单元测试
确认深藏在代码里面的某些功能仍然可用, 单元测试
能消除程序单元的不可靠性.
Codeception 的单元测试功能是基于 PHPUnit
之上的, 你可以照样写 PHPUnit
的测试代码, Codeception 一样能运行.
Codeception 在 PHPUnit
的基础上提供了一系列工具能让单元测试更加简单, 代码可读性更高. 单元测试是最复杂最繁琐的测试, 并且是会跟着业务逻辑代码的改变而改变, 在实际开发中技术人员会经常因为需求、业务的变更而修改单元测试, 提高其可读性和易用性可以帮助相关人员更加快速的跟上一切变化.
以下是一个简单的 integration test (集成测试)
<?php
function testSavingUser()
{
$user = new User();
$user->setName('Miles');
$user->setSurname('Davis');
$user->save();
$this->assertEquals('Miles Davis', $user->getFullName());
$this->unitTester->seeInDatabase('users',array('name' => 'Miles', 'surname' => 'Davis'));
}
优点
- 最快的测试, 当然, 在上面的示例代码中, 触碰到了数据库, 还是有点延迟;
- 能把测试覆盖到特别***钻的程序逻辑上, 这是
functional
或者acceptance
所做不到的; - 允许你测试最核心代码, 确定核心代码的健壮性;
- 写单元测试的程序要都是好程序员 。
缺点
- 因为是单元测试, 会把代码分为多个小单元单独测试, 但是各个单元之间的对接测试不到;
- 对代码的修改非常敏感, 很多项目的 test 最后没用上就是因为测试跟不上业务逻辑代码的修改.
进行api单元测试 (Codeception API testing) :
codeception的API测试模块
详细资料可以查阅官网文档: https://codeception.com/docs/10-APITesting
生成 api目录 和 api.suite.yml配置文件:
codecept generate:suite api
编辑api.suite.yml配置文件,原代码:
actor: ApiTester modules: enabled: - \Helper\Api
修改为:
actor: ApiTester modules: enabled: - REST: url: http://www.lancms.com depends: PhpBrowser part: Json
刷新文件生成:
codecept build
创建api测试单元文件:
codecept generate:cest api CreateUser
大概几秒后,就能看见tests/api目录下 新生成了 CreateUserCest.php 文件:
编辑 CreateUserCest.php 文件:
<?php class CreateUserCest { public function _before(ApiTester $I) { } // tests public function tryToTest(ApiTester $I) { } // tests public function createUserViaAPI(\ApiTester $I) { # 发送请求api地址: http://www.lancms.com/api/test/demo # 请求参数: {"name":"test","email":"test@163.com"} # 响应结果: {"code":1,"msg":"success","data":[]} $I->haveHttpHeader('accept', 'application/json'); // $I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded'); // 普通表单形式发送 $I->haveHttpHeader('Content-Type', 'application/json;'); // 发送JSON形式数据 $I->sendPOST('/api/test/demo', [ 'name' => 'test', 'email' => 'test@163.com', ]); $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200 $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); $I->seeResponseContains('success'); $I->seeResponseContainsJson([ "code" => "1", ]); $I->seeResponseJsonMatchesJsonPath("$.data"); $I->seeResponseJsonMatchesXpath('//data'); $I->seeResponseMatchesJsonType([ 'code' => 'integer', 'msg' => 'string', 'data' => 'string|array|null', ]); } }
执行测试api测试单元:
codecept run api
执行结果:
说明请求成功了,被请求的项目后台记录了请求信息:
codeception的generate生成文件命令列表:
generate:cept
generate:cest
generate:environment
generate:feature
generate:groupobject
generate:helper
generate:pageobject
generate:scenarios
generate:snapshot
generate:stepobject
generate:suite
generate:test
例如:
codecept generate:cest acceptance First
codecept generate:cept acceptance Second
解释:
codecept generate:cest acceptance First 这个命令将在 tests/acceptance/ 目录下生成一个文件 FirstCest.php
codecept generate:cest acceptance Second 这个命令将在 tests/acceptance/ 目录下生成一个文件 SecondCept.php
执行完 codecept generate:cest acceptance First , codecept generate:cept acceptance Second 命令完成后,再执行 codecept build 进行构建,最后 执行 codecept run 来跑测试命令:
codecept generate:cest acceptance First
codecept generate:cept acceptance Second
codecept build
codecept run acceptance
Cept, Cest 和 测试格式
Codeception支持3种测试格式,之前讲过的基于场景的Cept格式,Codeception也可以执行PHPUnit 单元测试测试文件和Cest格式。
Cest是OOP设计结合了场景驱动测试,如果你想组合一些测试场景到一个中你应该考虑使用Cest格式,下面的例子我们用一个单独的文件测试CRUD操作,但其中有几个测试,每次操作一个。
Codeception的cest和cept有什么区别:
他们的格式是唯一的区别: Cept是基于场景的格式, Cest是基于类的格式.
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');
非开发人员可能会发现Cept格式更加友善和平易近人. PHP开发人员可能更喜欢Cest格式,它能够支持每个文件的多个测试,并通过添加其他私有功能轻松重用代码.
最终只是一个品味,你可以选择你喜欢的格式.
Functional功能测试:
功能测试与其大部分相同,只有一点很大的不同:功能测试不必依赖一个运行的着的Web Server。
简单来说,我们设置 $_REQUEST
, $_GET
和 $_POST
变量然后再执行应用程序的测试,这最有价值的是功能测试更快并且当发生错误的时候可以提供更多的栈信息。
Codeception可以连接不同web框架做功能测试:Symfony2、Laravel4、Yii2、Zend Framework以及其它,你只需要在你的功能套件做相关配置,以使所需模块启动。
所有的这些框架的模块测试共享相同的接口,因此您的测试不受限于任何一个,这是一个功能测试示例。
使用命令来创建一个functional测试文件:
codecept generate:cept functional TestFunctional
将在 tests/functional/ 目录下生成一个 TestFunctionalCept.php 测试文件:
编写functional测试代码:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->click('Login'); $I->fillField('Username', 'Miles'); $I->fillField('Password', 'Davis'); $I->click('Enter'); $I->see('Hello, Miles', 'h1');
正如你看到的,你可以使用相同的方法做功能和验收测试。
运行functional功能测试:
codecept build
codecept run functional
上面用的是 generate:cept 来生成 功能测试文件: tests/functional/TestFunctionalCept.php
我们用 generate:cest 来生成 功能测试文件: tests/functional/LoginCest.php
codecept generate:cest functional Login
容易范的错误
验收测试通常比功能测试慢得多,但是功能测试不能把运行Codeception和应用程序在一个环境。假如你的应用程序不是设计运行在long living process中,例如你使用 exit
操作符或全局变量,那么功能测试并不适合你。
Headers, Cookies, Sessions
功能测试一个常见的问题就是使用PHP方法处理 headers
、 sessions
、 cookies
,正如你所知道的,多次执行同样的header header
方法会出发一个错误,在功能测试中我们多次运行应用程序,因此我们会在结果中有很多垃圾错误。
共享内存
功能测试不同于传统方法,在请求处理完成PHP应用程序是不会停止的。 所有请求运行在一个内存容器,他们不是孤立的。 因此如果你看到你的测试奇怪的失败而停止,那么你尝试执行一个单一的测试 这将检查是否在运行过程中分离独立测试,因为所有的测试都在共享内存中运行,它很容易被破坏。 保持你内存干净、避免内存泄漏、全局变量和静态变量干净。
使用框架模块
功能测试套件的目录是 tests/functional
,开始前需要在配置文件 tests/functional.suite.yml
中启用相应的框架模块。我们提供了配置流行的PHP框架的功能测试的操作指南。
写功能测试 (Functional):
写功能测试与 验收测试差不多,需要启用PhpBrowser
模块,所有的框架模块和 PhpBrowser
模块共享相同的方法和同意的引擎。
因此可以用 amOnPage
命令打开web页面。
<?php $I = new FunctionalTester; $I->amOnPage('/login'); ?>
我们可以用click link来打开应用程序页面。
<?php $I->click('Logout'); // click link inside .nav element $I->click('Logout', '.nav'); // click by CSS $I->click('a.logout'); // click with strict locator $I->click(['class' => 'logout']); ?>
我们也可以提交表单:
<?php $I->submitForm('form#login', ['name' => 'john', 'password' => '123456']); // alternatively $I->fillField('#login input[name=name]', 'john'); $I->fillField('#login input[name=password]', '123456'); $I->click('Submit', '#login'); ?>
以及断言:
<?php $I->see('Welcome, john'); $I->see('Logged in successfully', '.notice'); $I->seeCurrentUrlEquals('/profile/john'); ?>
框架模块在框架内也包含另外的方法,例如, Laravel4
、Phalcon1
和 Yii2
模块有seerecord
方法使用ActiveRecord层检查记录存在于数据库。 Laravel4
模块还包括方法做额外的检查,你可能发现 seeSessionHasErrors
对测试表单验证非常有用。
看看你真正使用的模块的完整参考资料,大多数的方法都是通用的,但其中一些事独特的。
你可以在一个测试里访问框架的全局变量,或访问Di容器里的HelperFunctional
类。
<?php namespace Helper; class Functional extends CodeceptionModule { function doSomethingWithMyService() { $service = $this->getModule('Symfony2')->grabServiceFromContainer('myservice'); $service->doSomething(); } } ?>
检查所用到的模块的所有的 Public属性 可以获取所有可用的方法。
错误报告
默认情况下Codeception使用的是E_ALL & ~E_STRICT & ~E_DEPRECATED
错误报告级别。 在功能测试中您可能需要自定义框架的错误级别,该错误报告级别可以设置在套件配置中:
actor: FunctionalTester modules: enabled: - \Helper\Functional error_level: "E_ALL & ~E_STRICT & ~E_DEPRECATED"
功能测试总结
如果你使用功能强大的框架,功能测试时非常棒的。通过使用功能测试,您可以通过功能测试,您可以访问和操作他们的内部状态。 这使得你的测试更快,在其它情况下,如果你不使用框架,就没有实际的理由来编写功能测试 如果你使用的是一个上述之外的框架,你可以创建一个模块到社区分享
运行测试( codecept run命令 )
通过运行 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 tests/acceptance/backend
上面命令是执行 tests/acceptance/backend 目录里的所有测试。
要执行一组存储不在同目录的测试时,你可以用groups 组织它们
我们获取详细的输出:
codecept run acceptance --steps
codecept run api -v
测试报告:
你可以通过 --xml 选项生成JUnit XML,通过--html输出HTML报告。
codecept run --steps --xml --html
上面这个命令将执行所有套件的测试,逐步显示,并且构建将HTML和XML报告存储到tests/_output/目录。
了解所有可用的选项运行这个命令:
php codecept.phar help run
调试
获取详细的输出,执行是加上--debug 选项,在测试中可以用那个 codecept_debug方法打印任何信息。
生成器
有很多有用的Codeception命令:
generate:cept
suite filename - Generates a sample Cept scenariogenerate:cest
suite filename - Generates a sample Cest testgenerate:test
suite filename - Generates a sample PHPUnit Test with Codeception hooksgenerate:phpunit
suite filename - Generates a classic PHPUnit Testgenerate:suite
suite actor - Generates a new suite with the given Actor class namegenerate:scenarios
suite - Generates text files containing scenarios from testsgenerate:helper
filename - Generates a sample Helper Filegenerate:pageobject
suite filename - Generates a sample Page objectgenerate:stepobject
suite filename - Generates a sample Step objectgenerate:environment
env - Generates a sample Environment configurationgenerate:groupobject
group - Generates a sample Group Extension
注释:
suite:用来生成 yml配置文件和目录
例如:生成 api目录 和 api.suite.yml配置文件:
codecept generate:suite api
查看更多详细命令列表: https://codeception.com/docs/reference/Commands#Run
结论
我们看到了Codeception的结构,大部分你需要的东西都可以通过bootstrap
命令生成,在你回顾了基本概念和配置之后,你可以写你的第一个测试场景了。
资料参考:
https://my.oschina.net/mafeifan/blog/697679
https://www.cloudxns.net/Support/detail/id/939.html
拓展阅读:
PHP自动测试框架Top 10 : https://my.oschina.net/u/2317468/blog/607775