標籤:
在這一章裡,我們將看到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 crmappcd crmappcurl -sS | phpgit init
配置測試載入器
正如我們在本章的開始部分所說,我們將遵從測試優先的開發實踐來進行驗收測試。這樣做的原因如下:
如果我們關心使用者特性請求的實現情況,我們需要一些形式的驗收測試。
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")時,我們只是為下面的測試行為建立了一個標題(用雙引號括起來的)。
讓我們修改一下代碼,加入煙霧測試 (Smoke Test)的功能,對我們的首頁進行測試:
$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: AcceptanceTestermodules: 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,放在項目的根目錄下。例如,如果你的web伺服器是Apache,應該把DocumentRoot指向web目錄。
因此,將下面的內容放在web目錄的index.php檔案中:
Our CRM
是的,就是7個字元的文字檔。畢竟,這就是我們的驗收測試期望的所有內容了,正確嗎?
讓我們來運行測試:
cept run
我們得到了如下輸出:
現在,我們需要在項目中使用Yii2了,描述我們功能需求的最佳方法,就是寫一個完整的端到端測試。
【翻譯】Yii2 第2章 用Yii2建立自訂應用(第1節)