Chinese (Simplified) (中文(简体)) translation by Soleil (you can also view the original English article)

如果你问,“什么是Yii?” 查看我之前的教程,Yii框架简介,该教程回顾了Yii的优点,并概述了2014年10月发布的Yii 2.0中的新功能。
在这个使用Yii2编程系列中,我指导读者使用Yii2 Framework for PHP。 在本教程中,我将使用与Yii2开发框架集成的Codeception探索自动化测试。
不可否认,我用代码编写测试的经验很少。 我经常参与资源有限的个人或小型项目。 在微软工作期间,我们有不同的测试团队来完成这项工作。 但坦率地说,这也很典型,对吧? 程序员喜欢编码,他们不写测试 - 至少老学校程序员没有。
Codeception是一个创新的库,其目的是使编写测试变得有趣和轻松。 而且,我会说他们已经取得了合理的成功。 当我把脚趾浸入“Lake Codeception”的水中时,它大多简单有趣。 但是,当我开始深入研究时,我遇到了Yii的配置问题以及本系列中使用的特定模块。 肯定存在挑战。 总的来说,我印象深刻,看到了更多学习的好处。
简而言之,Codeception及其与Yii的集成使我想要编写更多测试,这对我来说是第一次。 我怀疑你会有类似的经历。
在我们开始之前提醒一下,我确实参与了下面的评论主题。 如果您有其他想法或想为未来的教程建议主题,我会特别感兴趣。 如果您有问题或主题建议,请在下面发布。 您也可以直接通过Twitter @reifman与我联系。
入门
安装Codeception
为了指导我,我使用了Yii的测试环境设置文档。 我从全局安装代码开始,所以我可以在任何项目中使用它。
$ composer global require "codeception/codeception=2.1.*" Changed current directory to /Users/Jeff/.composer ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing symfony/yaml (v3.1.1) Loading from cache ... codeception/codeception suggests installing symfony/phpunit-bridge (For phpunit-bridge support) Writing lock file Generating autoload files
您还需要要求codeception/specify
:
$ composer global require "codeception/specify=*" Changed current directory to /Users/Jeff/.composer ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) composer require "codeception/verify=*" - Installing codeception/specify (0.4.3) Downloading: 100% Writing lock file Generating autoload files
和codeception/verify
:
$ composer require "codeception/verify=*" ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing codeception/verify (0.3.0) Downloading: 100% Writing lock file Generating autoload files
接下来,使用全局编写器目录为codecept
设置别名很有帮助:
$ composer global status Changed current directory to /Users/Jeff/.composer No local changes
这设置了别名:
$ alias codecept="/Users/Jeff/.composer/vendor/bin/codecept"
Yii还要求你安装Faker,它会为你的应用程序生成虚假的测试数据:
$ composer require --dev yiisoft/yii2-faker:* ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Nothing to install or update Generating autoload files
使用您的应用程序设置代码
Codecept bootstrap
初始化Yii应用程序的代码,创建各种配置文件,用于构建和运行针对您的应用程序的测试。 我们在本教程中使用本系列的Hello应用程序。 请参阅此页面上的GitHub链接以获取代码。
$ codecept bootstrap Initializing Codeception in /Users/Jeff/Sites/hello File codeception.yml created <- global configuration tests/unit created <- unit tests tests/unit.suite.yml written <- unit tests suite configuration tests/functional created <- functional tests tests/functional.suite.yml written <- functional tests suite configuration tests/acceptance created <- acceptance tests tests/acceptance.suite.yml written <- acceptance tests suite configuration tests/_output was added to .gitignore --- tests/_bootstrap.php written <- global bootstrap file Building initial Tester classes Building Actor classes for suites: acceptance, functional, unit -> AcceptanceTesterActions.php generated successfully. 0 methods added \AcceptanceTester includes modules: PhpBrowser, \Helper\Acceptance AcceptanceTester.php created. -> FunctionalTesterActions.php generated successfully. 0 methods added \FunctionalTester includes modules: \Helper\Functional FunctionalTester.php created. -> UnitTesterActions.php generated successfully. 0 methods added \UnitTester includes modules: Asserts, \Helper\Unit UnitTester.php created. Bootstrap is done. Check out /Users/Jeff/Sites/hello/tests directory
出于某种原因,我最终在hello / tests中找到了重复的测试目录; 只需删除hello/tests/functional,hello/tests/acceptance,hello/tests/unit就可以了。 所有测试都在hello/tests/codeception/ *中。
不同种类的测试
Codeception主要关注三种测试:
- 单元测试验证特定单元是否正常工作,例如对所有模型方法的详尽测试。
- 功能测试验证常见的应用程序方案,就好像用户正在执行它们,但使用Web浏览器模拟。
- 验收测试与功能测试相同,但实际上是通过Web浏览器运行测试。
它为您的测试代码支持三种不同的测试格式:
- Cept:这是最简单的单一场景测试文件
- Cest:一种面向对象的格式,用于在单个文件中运行多个测试
- 测试:在PHP测试框架PHPUnit上编写的测试
让我们从使用cept格式的验收测试示例开始:
验收测试
我们首先使用Codeception的Welcome
测试示例。
$ codecept generate:cept acceptance Welcome Test was created in /Users/Jeff/Sites/hello/tests/acceptance/WelcomeCept.php
这会生成tests/acceptance/WelcomeCept.php
,我们将在下面编辑。
由于验收测试需要浏览器,我们必须在项目中编辑/tests/acceptance.suite.yml以提供我们的开发URL,http:// localhost:8888/hello:
# Codeception Test Suite Configuration # # Suite for acceptance tests. # Perform tests in browser using the WebDriver or PhpBrowser. # If you need both WebDriver and PHPBrowser tests - create a separate suite. class_name: AcceptanceTester modules: enabled: - PhpBrowser: url: http://localhost:8888/hello/ - \Helper\Acceptance
现在,我们已准备好在tests/acceptance/WelcomeCept.php中修改初始测试。 我正在编写一个加载首页的测试,以确保它按预期工作。
代码测试具有actor的概念,在本例中,$I=new AcceptanceTester()
。
以下是它如何描述Codeception文档中的actor:
我们有一个UnitTester,它执行函数并测试代码。 我们还有一个FunctionalTester,一个合格的测试人员,他对整个应用程序进行测试,并了解其内部知识。 还有一个AcceptanceTester,一个通过我们提供的界面与我们的应用程序一起工作的用户。
您可以使用代码评论您的测试,例如 $I->wantTo('perform a certain test')
或 'ensure that the frontpage works'
.
在我的测试中,我想看到$I->see
'Congratulations!'
的文字。 和'Yii-powered'
:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('ensure that frontpage works'); $I->amOnPage('/'); $I->see('Congratulations!'); $I->see('Yii-powered');
这是当前的Hello主页:

接下来,让我们运行测试,只需codecept run
:
$ codecept run Codeception PHP Testing Framework v2.1.11 Powered by PHPUnit 5.3.5 by Sebastian Bergmann and contributors. Acceptance Tests (1) --------------------------------------------------------------------------------------- Ensure that frontpage works (WelcomeCept) Ok ------------------------------------------------------------------------------------------------------------ Functional Tests (0) ------------------------ --------------------------------------------- Unit Tests (0) ------------------------------ --------------------------------------------- Time: 554 ms, Memory: 10.25MB OK (1 test, 2 assertions)
如您所见,我们的测试通过了,验证此功能的代码非常易读且简单。
关于Yii默认测试的注释
为了更进一步,我开始使用Yii的默认测试。 此时,我遇到了许多配置问题 - 主要是由于我在本系列中使用了自定义yii2-user模块。 其他人则是由于Yii的小错误,其团队在GitHub上报告后迅速做出反应并修复; 在某些情况下,问题已在yii2-basic树的后续版本中得到修复。
另外,因为我更新了本系列的yii2-basic树,所以我不得不对一些默认测试做一些小改动。
以下是我做了一些小调整后运行默认验收测试的输出示例:
$ codecept run Codeception PHP Testing Framework v2.1.11 Powered by PHPUnit 5.3.5 by Sebastian Bergmann and contributors. Acceptance Tests (4) ------------------------------------------------------------------------------------------------- Ensure that about works (AboutCept) Ok Ensure that contact works (ContactCept) Ok Ensure that home page works (HomeCept) Ok Ensure that login works (LoginCept) Ok ----------------------------------------------------------------------------------------------------------------------
功能测试
为了使功能测试起作用,我需要运行Yii内置服务器的实例。 在Yii的Alex Markov在我们的GitHub交换中提到它之前,我还不知道这个组件。
$ ./yii serve
我对/tests/codeception /functional中的功能测试做了一些小改动,主要是为了查找我特定的更新文本字符串,即“无效的登录名或密码”代替Yii的默认值。 这是LoginCept.php的一个看看:
<?php use tests\codeception\_pages\LoginPage; $I = new FunctionalTester($scenario); $I->wantTo('ensure that login works'); $loginPage = LoginPage::openBy($I); $I->see('Login'); $I->amGoingTo('try to login with empty credentials'); $loginPage->login('', ''); $I->expectTo('see validations errors'); $I->see('Login cannot be blank.'); $I->see('Password cannot be blank.'); $I->amGoingTo('try to login with wrong credentials'); $loginPage->login('admin', 'wrong'); $I->expectTo('see validations errors'); $I->see('Invalid login or password'); $I->amGoingTo('try to login with correct credentials'); $loginPage->login('admin', 'admin11'); $I->expectTo('see user info'); $I->see('Logout');
基本上,代码访问LoginForm
模型并使用Yii serve测试其各种方法。
这是它正在利用的/tests/codeception_pages/LoginPage.php测试代码(我还必须对我们对该系列所做的更改进行修改):
class LoginPage extends BasePage { public $route = 'user/login'; /** * @param string $username * @param string $password */ public function login($username, $password) { $this->actor->fillField('input[name="login-form[login]"]', $username); $this->actor->fillField('input[name="login-form[password]"]', $password); $this->actor->click('button[type=submit]'); } }
您可以看到我们正在编写actor以fillFields
并click
按钮以获取更新的表单字段。
在解决我与Yii的Codeception集成问题时,我发现以详细模式运行这些测试很有帮助:
$ codecept run -vvv
以下是登录功能测试的详细输出 - 在MacOS终端中,PASSED
和FAILED
的颜色编码为红色或粉红色,并且为了可见性而反转:
Functional Tests (4) ------------------------------------------------------------------------------------------------- Modules: Filesystem, Yii2 ... ---------------------------------------------------------------------------------------------------------------------- Ensure that login works (LoginCept) Scenario: * I am on page "/index-test.php/user/login" [Page] /index-test.php/user/login [Response] 200 [Request Cookies] [] [Response Headers] {"content-type":["text/html; charset=UTF-8"]} * I see "Login" * I am going to try to login with empty credentials * I fill field "input[name="login-form[login]"]","" * I fill field "input[name="login-form[password]"]","" * I click "button[type=submit]" [Uri] http://localhost/index-test.php/user/login [Method] POST [Parameters] {"_csrf":"VEpvcmk3bVgFH1Y9AVsmYWQQDEouTSggYXMFGStdKBEnCyQfBxo8Bw==","login-form[login]":"","login-form[password]":""} [Page] http://localhost/index-test.php/user/login [Response] 200 [Request Cookies] {"_csrf":"dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:"_csrf";i:1;s:32:"QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_";}"} [Response Headers] {"content-type":["text/html; charset=UTF-8"]} * I expect to see validations errors * I see "Login cannot be blank." * I see "Password cannot be blank." * I am going to try to login with wrong credentials * I fill field "input[name="login-form[login]"]","admin" * I fill field "input[name="login-form[password]"]","wrong" * I click "button[type=submit]" [Uri] http://localhost/index-test.php/user/login [Method] POST [Parameters] {"_csrf":"QjFBRl9hMjMTZHgJNw15CnJrIn4YG3dLdwgrLR0Ld3oxcAorMUxjbA==","login-form[login]":"admin","login-form[password]":"wrong"} [Page] http://localhost/index-test.php/user/login [Response] 200 [Request Cookies] {"_csrf":"dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:"_csrf";i:1;s:32:"QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_";}"} [Response Headers] {"content-type":["text/html; charset=UTF-8"]} * I expect to see validations errors * I see "Invalid login or password" * I am going to try to login with correct credentials * I fill field "input[name="login-form[login]"]","admin" * I fill field "input[name="login-form[password]"]","admin11" * I click "button[type=submit]" [Uri] http://localhost/index-test.php/user/login [Method] POST [Parameters] {"_csrf":"bG8uMXdPYk49Ohd.HyMpd1w1TQkwNSc2WVZEWjUlJwcfLmVcGWIzEQ==","login-form[login]":"admin","login-form[password]":"admin11"} [Headers] {"location":["http://localhost/index-test.php"],"content-type":["text/html; charset=UTF-8"]} [Page] http://localhost/index-test.php/user/login [Response] 302 [Request Cookies] {"_csrf":"dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:"_csrf";i:1;s:32:"QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_";}"} [Response Headers] {"location":["http://localhost/index-test.php"],"content-type":["text/html; charset=UTF-8"]} [Redirecting to] http://localhost/index-test.php [Page] http://localhost/index-test.php [Response] 200 [Request Cookies] {"_csrf":"dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:"_csrf";i:1;s:32:"QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_";}"} [Response Headers] {"content-type":["text/html; charset=UTF-8"]} * I expect to see user info * I see "Logout" PASSED
总的来说,有一点需要学习如何开始使用Codeception并正确编写测试代码。 但结果令人印象深刻,乐于助人。
单元测试
基本上,单元测试是对我们的基础设施和模型的程序化测试。 理想情况下,我们会为模型的每种方法和使用变化编写测试。
不幸的是,我无法让单元测试在我们的树中工作,因为要发布的小Yii错误或者Codeception和yii2-user之间的配置问题我们已经集成在如何使用Yii2编程:集成用户注册。
Unit Tests (3) ---------------------------------------------------------------------------------------------------- Modules: ------------------------------------------------------------------------------------------------------------------- Trying to test login no user (tests\codeception\unit\models\LoginFormTest::testLoginNoUser)... <pre>PHP Fatal Error 'yii\base\ErrorException' with message 'Call to undefined function tests\codeception\unit\models\expect()'
我将在我们的Startup系列中再次讨论单元测试,该系列不使用yii2-user,而是使用Yii Advanced树的内置用户集成。
让我们看一下Yii2-app-basic树中的几个例子。
测试联系表格电子邮件
hello/tests/codeception/unit/models/ContactFormTest.php测试通过程序化使用模型发送电子邮件:
<?php namespace tests\codeception\unit\models; use Yii; use yii\codeception\TestCase; use Codeception\Specify; class ContactFormTest extends TestCase { use Specify; protected function setUp() { parent::setUp(); Yii::$app->mailer->fileTransportCallback = function ($mailer, $message) { return 'testing_message.eml'; }; } protected function tearDown() { unlink($this->getMessageFile()); parent::tearDown(); } public function testContact() { $model = $this->createMock('app\models\ContactForm', ['validate']); $model->expects($this->once())->method('validate')->will($this->returnValue(true)); $model->attributes = [ 'name' => 'Tester', 'email' => 'tester@example.com', 'subject' => 'very important letter subject', 'body' => 'body of current message', ]; $model->contact('admin@example.com'); $this->specify('email should be send', function () { expect('email file should exist', file_exists($this->getMessageFile()))->true(); }); $this->specify('message should contain correct data', function () use ($model) { $emailMessage = file_get_contents($this->getMessageFile()); expect('email should contain user name', $emailMessage)->contains($model->name); expect('email should contain sender email', $emailMessage)->contains($model->email); expect('email should contain subject', $emailMessage)->contains($model->subject); expect('email should contain body', $emailMessage)->contains($model->body); }); } private function getMessageFile() { return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml'; } }
由于Yii中尚未更新的小错误(或者至少我找不到更新的代码),我无法成功通过此测试。 我删除了Yii代码库,命名带有日期戳的出站邮件,上面的代码正在查找固定文件名。 因此,它总是失败。 尽管如此,看看程序化测试如何使用模型生成文件然后查找该文件并验证其内容以验证代码是否正常工作仍然很有用。
测试登录
我们来看看hello/tests/codeception/unit/models/LoginFormTest.php。 同样,在编写本教程时,我对yii2-user的使用使其过于难以集成; 但是,我们可以看一下单元测试用户模型函数的概念方法。
这是testLoginCorrect()
,它查看登录是否使用正确的密码成功:
public function testLoginCorrect() { $model = new LoginForm([ 'username' => 'admin', 'password' => 'admin11', ]); $this->specify('user should be able to login with correct credentials', function () use ($model) { expect('model should login user', $model->login())->true(); expect('error message should not be set', $model->errors)->hasntKey('password'); expect('user should be logged in', Yii::$app->user->isGuest)->false(); }); }
它使用LoginForm
模型以编程方式登录用户,然后以编程方式查看Yii的当前用户现在是否不再是来宾。
expect('user should be logged in', Yii::$app->user->isGuest)->false();
下一步是什么?
我希望你很喜欢学习Codeception及其与Yii的集成,尽管遇到了一些障碍。 今天yii2-basic的默认安装应该会更好。
如果您想了解更多关于决定测试时间和测试内容以及原因的信息,我建议您阅读Yii的测试概述。 当然还有更多关于Codeception和编写更完整测试的知识。
观看我们在Yii2系列编程中即将推出的教程,我们将继续深入研究框架的不同方面。 如果您想知道下一个Yii2教程何时到达,请在Twitter上关注我@reifman或查看我的教师页面。
您可能还想查看我们使用PHP系列构建您的初创公司,该系列在构建实际应用程序时使用Yii2的高级模板。 实际上,您今天可以试用启动应用程序Meeting Planner。
相关链接
- Codeception
- Yii2 Developer Exchange,我的Yii2资源网站
- Yii2测试环境设置
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post