标签:
在这一章里,我们将看到Yii2怎样帮助我们创建web应用。示例虽然很简单,但整个过程都符合软件工程思想。我们将完成应用开发的每一个步骤,并且每一步都会根据权威书籍中的最佳实践来进行:
创建领域模型:这本书解释了领域驱动,Tackling Complexity in the Heart of Software, Eric Evans, Addison-Wesley Professional
设置测试装置:我们遵照验收测试驱动实践,Growing Object-oriented Software, Guided by Tests, Steve Freeman and Nat Pryce, Addison-Wesley Professional
设置开发流水线
持续交付:Reliable Software Releases through Build, Test, and Deployment Automation, Jez Humble and David Farley, Addison-Wesley Professional
持续集成:Improving Software Quality and Reducing Risk, Paul M. Duvall, Steve Matyas, and Andrew Glover, Addison-Wesley Professional
红-绿-重构 开发循环:深度解释,请参考下列图书:
Clean Code:A Handbook of Agile Software Craftsmanship, Robert Martin, Prentice Hall
Test-Driven Development by Example, Kent Beck, Addison-Wesley Professional
部署和手工测试:这符合持续交付,并且这些步骤也是必不可少的
保持专注。
贯穿整本书,在使用示例应用的时候,我们需要注意实际需求。在这个小节,我们将定义整个示例的场景。
假设我们有一个小的商业应用,要对外提供一些服务。我们有一些客户,他们有一些数目巨大的账目记录在纸面上,用商业卡片管理非常不方便。因此,我们需要用自动化的方式把这些档案管理起来。
首先,我们需要一些增删查改(CRUD)界面进行记录管理,为客户展示最核心的属性。
很明显,我们的业务和客户可能会随着时间的推移而增长、变化,因此我们的应用也应该随之变化。在一开始,我们就应该为可能的变化做好准备。
本着吃自己狗食的原则(译注:微软在1998年提出),既然自己开发的系统自己要使用,那这个系统最好保证是高质量的。
很明显,我们将在应用中处理客户模型。在“customer”和“client”两个词中,我们觉得“customer”更为贴切。
一个客户(customer)是一个人,他至少具有姓名、地址、电子邮件,以及电话号码等属性。我们为客户提供一个服务,按小时统计,并根据合约支付这段时间产生的费用。这就是我们在第一次迭代设计中打算解决的问题。
我们假设每位客户都是单个的人,因此我们不用处理公司,有多个联系人的情况。姓名是一个复杂的结构,如果我们深入细节,有敬语、职务、昵称、中间名、姓等属性需要考虑。但在这个应用中,我们真正感兴趣的并不是客户的姓名,我们只是需要用姓名来识别一个客户。因此,我们将简单的使用一个文本行来表示,允许我们按任何格式来写入姓名。地址同样可以是一个复杂的结构,这次我们打算用一个结构来描述,而不再是一个简单的文本行。这是因为我们需要通过地址来实现以下两件事情。
进行一些统计,例如在一个特定的城市,有多少个客户
遵照不同的文化背景,正确地生成邮寄地址
因此,我们决定采用下面的结构:
用途(例如:账单地址、购物地址、家庭地址、工作地址)
国家
州,国家下级的区域,比如美国
城市
街道
建筑物
部门/办公室
收件人姓名
邮政编码
我们应该注意到,一个地址能代表一个部门、邮箱、办公室、组织中的雇员,甚至是整个建筑。同样,一个客户也可以多个地址。
电话这个实体具有以下属性:
用途(个人或工作)
号码
一个客户可能有几个电话号码,通过用途字段区分。
除了姓名、地址、电话这个实体之外,我们的职员需要通过一种途径对客户进行自由的描述。这里我们简化了姓名属性。然后,我们加入生日、电子邮件属性,当然,一个客户可以有多个电子邮件。
我们先停一下。
我们现在能画出客户模型的完整聚合图了,如下图所示:
根据Eric Evans的《领域驱动设计》,客户是一个实体(Entity),也就是说,是一个状态可以改变的对象,因此我们要关心它在整个系统中的一致性。而其它部分是值对象(Value Object),意味着初始创建后,状态不会改变的对象,因此他们是可以完全互换的。
为了简化处理,我们不再详细描述商业上是如何处理客户的,我们不打算在这本书中涵盖它。无论如何,让我们把注意力回到我们为客户提供的一系列服务中来,维护这些记录将会很有用。我们将在后面的章节用到这个模型。
让我们来执行一个特定的任务。考虑到有的人给我们打电话(假如我们已经识别出号码),我们想获取呼入者的所有详细信息。在应用中,如果这个号码没有找到关联的人,那我们就知道他并不是我们的客户。如果找到关联人,我们至少可以直接用他的姓名跟他打招呼,这是很好的客户服务。
我们应该理解数据库查询,我们需要一个途径可以插入数据,有可能还需要编辑和删除它。因此,我们第一次开发迭代的特性包含以下内容:
将客户信息插入数据库
在数据库中编辑客户信息
从数据库中删除客户信息
根据电话号码从数据库中查询客户信息
构造通用的数据库查询并不是我们的目标,我们仅仅需要根据一个电话号码进行查询。
让我们开始吧。
一直到本书结束,我们将在接下来的章节中讨论同一个应用,因此准备工作只需要做这一次。
下载示例代码:Packt的网站上可以下载你购买过图书的示例代码,地址是http://www.packtpub.com,如果你已经购买了这本书,请访问http://www.packtpub.com/support,注册后会直接将文件通过电子邮件发送给你。
我们要开发的应用,从本质上说,是一个客户关系管理系统(CRM)。因此,我们首先创建一个名为 crmapp 的目录。
请注意,正本书中,通过命令行调用的示例,都假定你当前目录为crmapp目录。
Yii2推荐使用包管理器Composer进行安装,因此我们就来使用这个工具。你可以去阅读Composer的完整文档了解详细的细节,我们在这里准备了一个简短的说明:
所有通过Composer安装的包,都会存放在项目路径下,一个名为vendor的子目录中。
与Composer相关的数据,所有依赖和其它信息,都保存在名为composer.json的清单文件中,位于项目根目录下。只要你在这个文件中申明了依赖,你就能在任何时候安全的删除vendor目录,并在调用php composer.phar install 或 php composer.phar update时完全重建。
Composer的文档描述了一个获取composer.phar的方法:
curl -sS https://getcomposer.org/installer | php
当然,如果你的PATH路径中如果没有CURL,你也可以直接去Composer的官方网站 https://getcomposer.org/ 直接下载PHAR文件。(译注:如果是windows环境,推荐直接下载一个Composer的安装包,安装完成后,添加到PATH目录,以后就可以直接使用composer <command>进行调用,而不使用php composer.phar <command>,更为方便)。
我们准备好后,就可以按下面的格式执行命令了:
php composer.phar <command>
假定你会使用版本控制系统来管理代码,本书的代码使用Git(http://git-scm.com/)进行管理。
准备阶段快速指引:
mkdir crmapp cd crmapp curl -sS | php git init
正如我们在本章的开始部分所说,我们将遵从测试优先的开发实践来进行验收测试。这样做的原因如下:
我们想检查应用是否能正常工作,又不想使用乏味的人工测试
我们还没有深度的单元测试需求,因为我们目前主要的工作只是把已经存在的组件装配在一起,因此,通过对UI进行端对端的验收测试最为简单可行
如果我们关心用户特性请求的实现情况,我们需要一些形式的验收测试。
Yii2内置支持Codeception测试框架,官方站点是 http://codeception.com/。我们不会在本身中直接使用它,但是 yii2-codeception(https://github.com/yiisoft/yii2-codeception)提供了一些辅助类,来集成到Yii框架中。
让我们来申明,我们需要在项目中使用 Codeception,执行下面的命令:
php composer.phar require "codeception/codeception:*"
稍等片刻,直到Composer执行完成。
目前,composer.json文件的内容是:
{ "require": { "codeception/codeception": "*", } }
命令 php composer.phar require <包名:版本> 只是一个辅助方法,将require块插入到清单文件中,并调用update命令。
当然,我也需要将Yii2同样作为依赖加入,但在这之前,让我们先干一件事情。
正如我们看到的,Codeception已经存在于 ./vendor/bin/codecept 目录中了。这个路径敲起来比较长,在POSIX兼容的shell中,比如bash,允许我们使用下面的方式来进行简化:
alias cept="./vender/bin/codecept"
这样做更好。在本章余下的部分,我们都假定你已经执行了上面这条命令。
Codeception是一个复杂的系统,所以我们需要依赖它自己内建的命令。没必要深入Codeception的内部细节,我们只是简单实用就可以了。执行下面的命令:
cept bootstrap
这将为Codeception生成一个tests目录,并进行配置。
现在,让我们创建一个傻瓜型的验收测试,用以检查我们的测试工具。
cept generate:cept acceptance SmokeTest
这个命令将生成 SmokeTestCept.php,位于 tests/acceptance 目录下。当我们打开这个文件,我们将看到如下代码(根据Codeception版本,代码可能略有不同):
$I = new AcceptanceTester($scenario); $I->wantTo(‘perform actions and see result‘);
AcceptanceTester是一个可以模仿浏览器后的真实用户,对应用进行测试的类。Codeception同时也提供 CodeGuy 用以进行单元测试,提供 TestGuy 进行功能测试,在后面我们才会用到。
当我们调用 AcceptanceTester.wantTo("do something")时,我们只是为下面的测试行为创建了一个标题(用双引号括起来的)。
让我们修改一下代码,加入冒烟测试的功能,对我们的首页进行测试:
$I = new AcceptanceTester($scenario); $I->wantTo(‘See that landing page is up‘); $I->amOnPage(‘/‘); $I->see(‘Our CRM‘);
当我们访问应用首页的时候,我们希望看到有 Our CRM 这一行。假设我们应用中已经有那么一个标题存在了。
现在运行测试:
cept run
我们会看到失败的信息,因为我们还没有设置web服务器,处理 / 请求。这样,到了该我们写一些生产代码来通过这个测试的时候了。然而,到目前而言,我们需要的还不是生产代码,我们的基础服务都还没有建立起来。我们需要先建立发布机制。
我们已经说过,编写的web验收测试会模拟真实用户,在浏览器中打开应用,通过可视的UI进行交互。因此,我们现在需要做的是将应用整个部署到我们能运行验收测试的机器。
提示:最常见的情况是,你决定开发和测试使用同一台机器,这是错误的!别这么干。
很可能你的工作平台和应用最终要运行的机器不是一样的。参照已有数十年历史的工业生产,这已经是一个持续不断的问题了。当应用的生命周期预计是数年时,在不同环境的生产服务器上测试你的应用,你会面临相似的集成问题。当然,这不是将打包好的软件卖给用户,这还是要简便一些。在我们的案例中,我们假定一个单一的发布点只有一个固定web应用,因此,简便性不是问题,可重复性测试才是问题。
最终,你的验收测试将会包含以下几个步骤:
将应用发布到测试服务器
在你的机器上运行验收测试
当然,你可以在测试服务器上运行验收测试。要这样做,你只需要用localhost这个本地回路网络接口来配置测试就可以了。然而,还是需要你安装一些与测试服务器不相干的软件。例如,如果你想全栈运行,使用Selenium进行浏览器内测试,你可能需要安装一个web浏览器,Java运行时,虚拟帧缓冲软件,并且这又需要安装它们各自相关的软件,这样做非常费劲。最有效的做法是使用你自己的桌面环境,来运行web验收测试。
提示:这当然不是在谈论单元测试和功能测试。单元测试由于只与本身有关,应在有原始代码的地方执行,完全不需要部署。功能测试,应在部署后的应用上测试,因为需要测试经过配置后的最终应用以及交互的正确性。
在任何案例中,理想化的方案是,你只需一个就像deploy这样的简单命令,就能完成以下功能:
访问并启动目标机器(特别是,当它是一个虚拟机实例的时候)
确保除应用外,环境是有效的
将当前代码拷贝到目标机器
在目标机器上,配置刚拷贝过去的代码的环境
启动应用
你应该能在命令行中输入deploy,并敲击Enter键,完成上述的所有步骤。正如Martin Fowler在他的《持续集成》(http://martinfowler.com/articles/continuousIntegration.html)中所说,这对你来说小菜一碟。理想情况下,部署行为应该在你启动验收测试工具时自动完成。
在本书中,我们只关心最后两步。因为我们开发的是PHP应用,只要目标机器上的web服务器在运行,”启动应用“这一步就算已经完成了。
本书仅关注web应用开发,并不涉及系统管理,那是系统管理员的事。然而,在附录A,”使用Vagrant进行部署设置“,我们准备介绍一个基于虚拟机的部署,同样也很容易在任何桌面工作站上实现。你可以不必需要另外一台物理机,就仍然能模拟现实世界的部署过程。如果你别无选择,强烈推荐你读读。实际上,采用那里的描述,本身所有的代码都准备好了。让我们假设你有一个准备好的环境和deploy命令,为了简化,我们同时假定每次运行验收测试前,部署都会自动运行。部署的结果可以从你的机器通过一个简单的URL进行访问,验收测试工具会将它作为应用的入口点。
现在,让我们到Codeception的跟验收测试相关的配置部分,打开文件 tests/acceptance.suite.yml,把入口URL加入到 modules.config.PhpBrowser.url 词条中。假定你没修改过这个文件,文件内容看起来应该像这样:
class_name: AcceptanceTester modules: enabled: - PhpBrowser - WebHelper config: PhpBrowser: url: ‘http://YOUR.APPLICATION.URL‘
(译注:本机acceptance.suite.yml文件格式跟上面不同,不存在config项,url直接放在enabled的PhpBrowser下)
举例来说,如果你用Apache的基于IP的虚拟主机技术来配置目标机器(https://httpd.apache.org/docs/2.2/vhosts/ip-based.html),modules.config.PhpBrowser.url 的值就应该像这样:http://127.0.0.01:8000。
当我们改变配置,我们需要重建Codeception,执行下面的命令:
cept build
不要忘记 cept 这个别名是我们自己创建的。实际运行的是 ./vendor/bin/codecept 文件。
如果你现在运行测试:
cept run
你应该可以看到如下输出:
你会看到Codeception在 / 路由上显示了一些东西,但还不是我们期望的。根据使用的Apache版本不同,可以是404错误或者403错误。如果你使用的其它Web服务器,可能显示的另外的错误信息。无论如何,问题的根源很简单,这就是我们需要在外界可访问的web目录中,加入index.php文件。
我们先做一个约定,可以被外部访问到的目录只有一个,命名为web,放在项目的根目录下。例如,如果你的web服务器是Apache,应该把DocumentRoot指向web目录。
因此,将下面的内容放在web目录的index.php文件中:
Our CRM
是的,就是7个字符的文本文件。毕竟,这就是我们的验收测试期望的所有内容了,正确吗?
让我们来运行测试:
cept run
我们得到了如下输出:
现在,我们需要在项目中使用Yii2了,描述我们功能需求的最佳方法,就是写一个完整的端到端测试。
【翻译】Yii2 第2章 用Yii2创建自定义应用(第1节)
标签:
原文地址:http://my.oschina.net/u/870995/blog/508941