本文概述
在继续进行Codeception和PHP之前, 我们应该介绍基础知识, 并首先说明为什么首先需要在应用程序中进行测试。也许我们至少可以在不浪费时间的情况下完成一个项目?
当然, 你不需要对所有内容进行测试;例如, 当你要建立另一个首页时。当你的项目包含一台路由器链接的静态页面时, 你可能不需要测试。
但是, 你绝对需要在以下情况下进行测试:
- 你的团队使用BDD / TDD。
- 你的Git存储库包含多个提交。
- 你是一个合适的专业人员, 正在从事一个严肃的项目。
你可以说自己已经有一个专门的测试部门, 这是一组进行测试并在需要时编写新测试的人员。但是, 你能想象在向项目中添加新功能后需要花费多长时间进行错误修复吗?
测试解决了什么?
首先, 让我们决定通过测试可以解决哪些类型的问题。你无法摆脱测试中的所有错误, 但可以描述测试用例中的预期行为。错误可能在你的测试用例中。即使你使用测试用例, 意大利面条式代码仍然是意大利面条式代码。
但是, 你可以确定之后将更改你的代码(通过修复错误或添加新功能), 因此你的代码仍将没有测试中描述的错误。此外, 有时甚至可以在文档中使用编写良好的测试, 因为你可以在其中查看典型方案的展开方式并检查预期的行为。可以说, 测试是未来很小但至关重要的投资。
那么我们可以采用哪种测试呢?
基本单元测试通常是不够的。它们需要集成, 功能和验收测试的支持。
鸣叫
- 单元测试:低级测试, 用于检查代码的一小部分-类的方法与其他代码隔离。
- 集成测试:集成测试检查应用程序的一部分, 它们可能包含多个类或方法, 但应仅限于一个功能。此测试还应检查不同类之间的交互方式。
- 功能测试:测试对你的应用程序的特定请求:浏览器响应, 数据库更改等。
- 验收测试:在大多数情况下, 验收测试意味着检查应用程序是否满足所有客户要求。
为了澄清起见, 假设我们用有形的东西(例如建筑物)来说明该过程。建筑物由形成墙的小块组成。每块砖必须满足规定的要求;它必须承受所需的载荷, 具有特定的体积和形状, 等等。这些是单元测试。集成测试的目的是检查砖块之间是否紧密紧密地相互粘附, 以及如何将它们集成到建筑物的特定元素中。可以将功能测试比作在建筑物的一面墙上进行测试, 以检查内部是否受到保护而不受元素影响, 以及是否可以透过窗户看到阳光。验收测试涉及将整个建筑物作为一个完整的产品进行测试:打开门, 进入室内, 关上门, 打开灯, 爬到二楼, 然后观察建筑物外的花园。
满足代码接收
但是, 这种划分是有条件的, 有时很难抵制混合各种测试的诱惑。
许多开发人员使用单元测试并声称足够了。我曾经是这样的一名开发人员。我发现针对不同类型的测试使用不同的系统既困难又耗时。不久前, 我决定找到比PHPUnit更有用的东西。我想更好地测试我的代码, 但是我不想阅读和学习大量文档并寻找陷阱。这就是我发现Codeception的方式。一开始, 我很怀疑, 就像我们经常遇到的新事物一样(该项目已有五年历史, 因此从技术上讲, 它不能被视为”新”项目), 但是在试用了几天之后, 我断定Codeception是一个非常有用且功能强大的系统。
那么如何安装Codeception?它变得如此简单:
$ composer require "codeception/codeception"
$ php vendor/bin/codecept bootstrap
安装后, 你将在项目中找到一个名为tests的新文件夹, 并且会有一些子文件夹分别名为acceptance, functional和unit。看来我们可以开始编写测试了。太好了, 但是接下来呢?
现在, 尝试添加一个标准的Hello World测试。
$ php vendor/bin/codecept generate:cept acceptance HelloWorld
现在, 我们得到一个验收测试文件tests / acceptance / HelloWorldCept.php, 内容如下:
<?php
$I = new AcceptanceTester($scenario);
$I->wantTo('perform actions and see result');
默认变量$ I不只是一个字母;这是一个角色。什么进行测试?测试人员, 显然。该测试人员将打开你网站的页面或课程, 并对其进行处理, 并向你显示其操作的最终结果。你将看到有效的方法和错误的方法。这就是为什么该对象被命名为$ I的原因, 并且它包含了名为wantTo(), see()或amOnPage()的方法。
因此, 让我们想像测试人员那样来检查页面可操作性的方法。第一种方法是打开页面并搜索短语。证明该页面可供访问者使用。
这应该很容易:
<?php
$I->amOnPage('/');
$I->see('Welcome');
我们可以使用此命令来运行Codeception的测试:
$ php vendor/bin/codecept run
我们立即看到有问题。乍一看, 消息似乎太长且不清楚, 但是当我们仔细观察时, 一切都会变得显而易见。
哎呀!出问题了。这就是测试的重点。查看消息, 确定错误, 并从错误中学习。
鸣叫
我们进行了一项测试, 即验收, 它检测到一个错误:
Acceptance Tests (1)
Perform actions and see result (HelloWorldCept) Error
----------
1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php)
[GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
这是罪魁祸首:本地主机不可用。
这是我们测试的场景步骤:
1. $I->amOnPage("/")
好的, 让我们打开tests / acceptance.suite.yml并将网址:http:// localhost /更改为实际可用的内容。就我而言, 这是我的本地测试主机, URL:https://local.codeception-article.com/
再次运行测试, 这应该是最终结果:
Acceptance Tests (1) ---------------------------------------------------------------------------------------
Perform actions and result (HelloWorldCept) Ok
万岁!我们的第一个成功测试!
当然, amOnPage()并不是唯一可用的测试方法, 我们仅将其作为示例。所有Codeception测试方法可以分为以下几组:
- 与页面的交互:fillField(), selectOption(), submitForm(), click()
- 断言。 see(), dontSee(), seeElement(), seeInCurrentUrl(), seeCheckboxIsChecked(), seeInField(), seeLink()。你可以为所有这些方法添加一个后缀, 并在需要一种在找不到某些内容时不会中断测试方案的方法时使用它。
- Cookie方法:setCookie(), grabCookie(), seeCookie()
- 测试方案的注释和描述:amGoingTo(), wantTo(), Expect()。使用这些方法可以很好地评论和描述测试, 这将帮助你记住测试的目标。
因此, 如果我们要测试密码重置电子邮件页面, 则可以通过以下方式进行操作:
<?php
$I = new AcceptanceTester($scenario);
$I->wantTo('Test forgotten password functionality');
$I->amOnPage('/forgotten')
$I->see('Enter email');
$I->fillField('email', '[email protected]');
$I->click('Continue');
$I->expect('Reset password link not sent for incorrect email');
$I->see('Email is incorrect, try again');
$I->amGoingTo('Fill correct email and get link');
$I->see('Enter email');
$I->fillField('email', '[email protected]');
$I->click('Continue');
$I->expect('Reset password link sent for correct email');
$I->see('Please check your email for next instructions');
看起来应该这样做, 但是如果页面上有一些Ajax加载的部件怎么办?我们可以测试这样的页面吗?答案是默认情况下, Codeception使用基于Symfony BrowserKit和Guzzle的PhpBrowser。它简单, 快速, 你只需要卷曲即可使用。
你也可以在实际的浏览器中使用Selenium并测试页面。是的, 它会比较慢, 但是你也可以测试JavaScript。
首先, 你需要安装Selenium驱动程序, 更改accept..suite.yml并重建AcceptanceTester类。之后, 你可以使用方法wait()和waitForElement()。而且, 更有趣的是, 你将能够使用方法saveSessionSnapshot()和loadSessionSnapshot()来节省时间和资源。此方法允许你存储会话状态并在较早的会话中启动新测试。在某些情况下, 例如在测试授权过程中, 这很有用。
因此, 我们最终获得了一种简单而强大的功能来测试许多功能。
功能测试
好, 该继续进行功能测试了。
$ php vendor/bin/codecept generate:cept functional HelloWorld
这就是我们得到的:
<?php
$I = new FunctionalTester($scenario);
$I->amOnPage('/');
$I->see('Welcome');
等一下
不, 这不是错误。功能测试的编写方式应与集成测试的编写方式相同。区别在于功能测试直接与你的应用程序进行交互。这意味着你不需要网络服务器即可运行功能测试, 并且拥有更多的能力来测试应用程序的不同部分。
这的确意味着缺少对所有框架的支持, 但是受支持的框架列表非常广泛:Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen和Laravel。对于大多数情况和大多数开发人员而言, 这应该足够。请查阅Codeception的模块文档以获取可用功能列表, 然后只需在functional.suite.yml中将其打开。
Codeception支持主要框架:Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen和Laravel。
鸣叫
在进行单元测试之前, 请允许我先谈谈。你可能已经注意到, 我们使用关键cept创建了测试:
$ php vendor/bin/codecept generate: cept acceptance HelloWorld
这不是创建测试的唯一方法。也有cest测试。区别在于你可以在一类中构造多个相关方案:
$ php vendor/bin/codecept generate:cest acceptance HelloWorld
<?php
class HelloWorldCest
{
public function _before(AcceptanceTester $I)
{
$I->amOnPage('/forgotten')
}
public function _after(AcceptanceTester $I)
{
}
// tests
public function testEmailField(AcceptanceTester $I)
{
$I->see('Enter email');
}
public function testIncorrectEmail(AcceptanceTester $I)
{
$I->fillField('email', '[email protected]');
$I->click('Continue');
$I->see('Email is incorrect, try again');
}
public function testCorrectEmail(AcceptanceTester $I)
{
$I->fillField('email', '[email protected]');
$I->click('Continue');
$I->see('Please check your email for next instructions');
}
}
在此示例中, 方法_before()和_after()在每个测试之前和之后运行。每个测试都会传递一个AcceptanceTester类的实例, 因此你可以像在cest测试中一样使用它。这种测试样式在某些情况下可能很有用, 因此值得牢记。
单元测试
是时候进行一些单元测试了。
Codeception基于PHPUnit, 因此你可以使用为PHPUnit编写的测试。要添加新的PHPUnit测试, 请使用以下方法:
$ php vendor/bin/codecept generate:phpunit unit HelloWorld
或者只是继承\ PHPUnit_Framework_TestCase上的测试。
但是, 如果你需要更多功能, 则应尝试Codeception的单元测试:
$ php vendor/bin/codecept generate:test unit HelloWorld
<?php
class HelloWorldTest extends \Codeception\TestCase\Test
{
/**
* @var \UnitTester
*/
protected $tester;
protected function _before()
{
}
protected function _after()
{
}
// tests
public function testUserSave()
{
$user = User::find(1);
$user->setEmail('[email protected]');
$user->save();
$user = User::find(1);
$this->assertEquals('[email protected]', $user->getEmail());
}
}
目前没有什么异常。方法_before()和_after()是setUp()和tearDown()类似物, 将在每次测试之前和之后运行。
该测试的主要优势在于它能够通过包含可在unit.suite.yml中打开的模块来扩展测试过程:
- 访问内存缓存和数据库以跟踪更改(支持MySQL, SQLite, PostgreSQL, MongoDB)
- REST / SOAP应用程序测试
- Queue列
每个模块都有其自己的功能, 因此最好在进行实际测试之前检查文档并收集每个模块的必要信息。
另外, 你可以使用Codeception / Specify包(需要将其添加到composer.json中)并编写如下描述:
<?php
class HelloWorldTest extends \Codeception\TestCase\Test
{
use \Codeception\Specify;
private $user;
protected function _before()
{
$this->user = User::find(1);
}
public function testUserEmailSave()
{
$this->specify("email can be stored", function() {
$this->user->setEmail('[email protected]');
$this->user->save();
$user = User::find(1);
$this->assertEquals('[email protected]', $user->getEmail());
});
}
}
这些闭包函数内部的PHP代码是隔离的, 因此内部的更改不会影响其余代码。描述将帮助你使测试更具可读性, 并易于识别失败的测试。
作为可选的附加功能, 你可以将Codeception \ Verify包用于类似BDD的语法:
<?php
public function testUserEmailSave()
{
verify($map->getEmail())->equals('[email protected]');
}
当然, 你可以使用存根:
<?php
public function testUserEmailSave()
{
$user = Stub::make('User', ['getEmail' => '[email protected]']);
$this->assertEquals('[email protected]', $user->getEmail());
}
结论:Codeception节省了时间和精力
那么你对Codeception会有什么期望?是给谁用的?有什么警告吗?
具有不同PHP水平和不同规模团队的开发人员可以使用Codeception。
鸣叫
我认为, 该测试框架适用于各种不同的团队:大小不一, 初学者和经过战斗的PHP专业人员, 正在使用流行框架的人员和未使用任何框架的人员。
无论如何, 一切都归结为这一点:编码接收已准备就绪。
它是一个成熟且有据可查的框架, 可以通过许多模块轻松扩展。代码接收是现代的, 但是基于经过时间考验的PHPUnit, 这可以使不想进行过多试验的开发人员放心。
它运行良好, 这意味着它运行很快, 不需要太多时间和精力。更好的是, 它相对容易掌握, 并且大量的文档应该可以帮助你轻松地进行学习。
Codeception也易于安装和配置, 但是拥有许多高级选项。尽管大多数用户并不需要全部(或确实是大多数), 但这完全取决于你打算如何处理。你可以从基础开始, 额外的功能迟早会派上用场。
相关:Buggy PHP代码:PHP开发人员最常犯的10个错误
评论前必须登录!
注册