在使用持續集成之前,很多開發團隊都是用每日構建(nightly build)。當時,微軟使用這個實踐很多年了。誰破壞了構建,就要負責監視后續的構建構成,直至發現下一個破壞了構建的人。
對于大多數項目來說,采納持續集成實踐是向高效率和高質量邁進的一大步。它保證那些創建大型復雜系統的團隊具有高度的自信心和控制力。一旦代碼提交引入了問題,持續集成就能為我們提供快速的反饋,從而確保我們作為一個團隊所開發的軟件是可以工作的。 持續集成的主要關注對象是開發團隊。持續集成系統的輸出通常作為手工測試流程和后續發布流程的輸入。在軟件的發布過程中,很多浪費來自于測試和運維環節。 例如:我們常常看到:
采取一種更完整的端到端的方法來交付軟件。我們通過一鍵式方式把軟件的某個版本部署好,甚至可以將其一鍵式部署到生成環境中,這樣就可以建立了一個非常有效的反饋環--由于很容易將應用程序部署到測試環境中,所以團隊可以同時得到軟件功能和部署流程兩個方面的快速反饋。因為部署流程(無論是測試,集成)都是自動化的,所以可以頻繁且有規律的運行并被測試,從而降低發布風險,也降低了向開發團隊傳遞有關部署流程的知識時的風險。
從精益的角度來看,我們實現了一個"拉式系統",測試團隊只要自己單擊按鈕,就能輕松部署。管理人員也能容易得到一些關鍵的度量指標,比如周期時間,吞吐量以及代碼質量。
什么是構建:在現代軟件學中,可能經常聽到CMMI,ISO(大型團隊)、中小型團隊(敏捷開發、XP編程)
構建工具:MSBuild、Maven、Ant、NAnt、Gradle
Unit是整個金字塔的基石(在建筑行業,基石是做建筑物基礎的石頭),如果基石不穩,Service和UI何談有構建意義呢?只有基石穩如磐石,上層建筑才夠堅固。本來想拿瑞士做鐘表的例子來說明下,但同事說的汽車例子更好。一輛汽車由許多配件組成,如果有以下兩種選擇,你會選擇哪個呢?
1. 所有單元配件沒有測試過,在4S店,銷售人員告訴你:剛組裝好,已經開了一天,能跑起來,你可以試試;2. 所有單元配件在生產過程已經經過嚴格測試,在4S點,銷售人員告訴你,已經通過國家認證,出廠合格,有質量保證,你可以試試;
MSBuild 是 Microsoft 和 Visual Studio的生成系統。它不僅僅是一個構造工具,應該稱之為擁有相當強大擴展能力的自動化平臺。MSBuild平臺的主要涉及到三部分:執行引擎、構造工程、任務。其中最核心的就是執行引擎,它包括定義構造工程的規范,解釋構造工程,執行“構造動作”;構造工程是用來描述構造任務的,大多數情況下我們使用MSBuild就是遵循規范,編寫一個構造工程;MSBuild引擎執行的每一個“構造動作”就是通過任務實現的,任務就是MSBuild的擴展機制,通過編寫新的任務就能夠不斷擴充MSBuild的執行能力。所以這三部分分別代表了引擎、腳本和擴展能力。MSBuild的簡單介紹與使用
配置管理:使用版本控制版本控制系統(源代碼控制管理系統)是保存文件多個版本的一種機制。一般來說,包括Subversion、Git在內的開源工具就可以滿足絕大多數團隊的需求。所有的版本控制系統都需要解決這樣一個基礎問題: 怎樣讓系統允許用戶共享信息,而不會讓他們因意外而互相干擾?如果沒有版本控制工具的協助,在開發中我們經常會遇到下面的一些問題:一、 代碼管理混亂。二、 解決代碼沖突困難。三、 在代碼整合期間引入深層BUG。四、 無法對代碼的擁有者進行權限控制。五、 項目不同版本發布困難。
對所有內容都進行版本控制
版本控制不僅僅針對源代碼,每個與所開發的軟件相關的產物都應該被置于版本控制下,應當包括:源代碼、測試代碼、數據庫腳本、構建和部署腳本、文檔、web容器(tomcat的配置)所用的配置文件等。
保證頻繁提交可靠代碼到主干
頻繁提交可靠、有質量保證的代碼(編譯通過是最基本要求),能夠輕松回滾到最近可靠的版本,代碼提交之后能夠觸發持續集成構建,及時得到反饋。
提交有意義的注釋
強制要求團隊成員使用有意義注釋,甚至可以關聯相關開發任務的原因是。當構建失敗后,你知道是誰破壞了構建,找到可能的原因及定位缺陷位置。這些附加信息,可以縮短我們修復缺陷的時間。示例:團隊使用了svn和redmine。
有可以加強的部分
- Git要求每次提交都必須寫提交說明,可以借鑒
- 測試代碼、數據庫腳本單獨分支
- 構建腳本化
這個流程的起點是開發人員向版本控制庫提交代碼。此時,持續集成系統對這次提交做出響應,觸發該流水線的一個實例。第一個階段會編譯代碼,運行單元測試,執行代碼分析,創建軟件二進制包。如果所有的單元測試都通過了,并且代碼符合編碼標準,就將可執行代碼打包成可執行文件,并放到一個制品庫里中。有些在提交而極端,還會執行另外一些任務,比如為驗收測試準備數據庫。第二個階段由運行時間較長的自動化驗收測試組成。所以持續集成服務器最好支持將測試分成多組的做法,以便在構建網絡中并行執行任務,提高執行效率。這個階段在第一個階段完成后自動觸發的。部署流水線可能有分支出現,這樣就可以將該構建版本獨立部署到多個不同的環境中,比如部署到用戶驗收環境、容量測試環境和生成環境。這時,部署到相應的環境,就需要用到自動化部署腳本來執行這種部署過程。測試人員應當能看到需要測試的所有構建版本,以及他們的狀態。目的:最快的得到反饋
3.2部署流水線的相關實踐
- 只生成一次二進制包我們將所有可執行代碼的集合稱作二進制包,例如.NET程序集。有時候代碼不需要編譯,那么這種情況下,二進制是所有文件的集合。一種相關的反模式就是一直使用源代碼而不是二進制包。因此,每次講一個修改部署時,需要自己親自從發布分支遷出源代碼并重新編譯二進制包。同時,還可能因編譯器和某個依賴的不同版本產生差異。
部署的可視化
測試分類:
BDD,TDD,ATDDBDD主要是基于場景、面向需求的,ATDD面向驗收的,這里不做過多介紹了。這里主要介紹TDD的一些開發
測試驅動開發
TDD的優點TDD從一開就保證了代碼的質量:鼓勵開發人員只開發”最小化“的代碼完成特定測試功能。遵循SOLID原則: SOLID (單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉)TDD確保了代碼與業務需求之間的高度一致性。TDD鼓勵創建更簡單,針對性更強的APITDD鼓勵更多的溝通,與企業,與團隊內部。TDD有助于清除冗余代碼TDD提供了內置的回歸測試。
如果沒有單元測試來幫助查找和診斷缺陷時,大多數開發人員會使用調試器,在他們認為出現缺陷的地方設置斷點,有時將這種方法稱為"散彈槍方法"。
單元測試的特征:
- 與其他代碼隔離
- 有針對性
- 與其他開發人員隔離
- 可重復
- 可預測
提交代碼、運行測試的重點是什么?快速捕獲那些因修改向系統中引入的最常見錯誤,并通知開發人員,以便他們能快速修復他們。提交階段提供反饋的價值在于,對它的投入可以讓系統高效且更快地工作。
常規一個加密、解密方法的測試
[Test] [Ignore("這個測試有問題!")] public void TestEncrypt() { FileEncrypt fileEncrypt = new FileEncrypt(); String ii = fileEncrypt.EncryptContext("Hello World", "123"); ii = fileEncrypt.DecryptContext(ii, "123"); Assert.Pass(ii); }
Ignore標志的方法,當我們運行測試實例時,可以忽略Category
可以只運行指定目錄的測試用例Rhino.Mocks的簡單實用
[Test] [Category("模擬對象")] public void TestCustomer() { MockRepository mocks = new MockRepository(); ICustomer customer = mocks.StrictMock<ICustomer>(); customer.Expect(c => c.ShowTitle("")).Return("567"); Expect.Call(customer.Pid).Return(30); customer.Replay(); Assert.AreEqual(customer.ShowTitle(""), "567"); } [Test] public void SayHelloWorld() { MockRepository mocks = new MockRepository(); INameSource nameSource = mocks.DynamicMock<INameSource>(); Expect.Call(nameSource.CreateName(null, null)) .IgnoreArguments() .Do(new NameSourceDelegate(Formal)); mocks.ReplayAll(); const string expected = "Hi, my name is Ayende Rahien"; string actual = new Speaker("Bright", "Gong", nameSource).Introduce(); Assert.AreEqual(expected, actual); } delegate string NameSourceDelegate(string first, string surname); public class Speaker { PRivate readonly string _firstName; private readonly string _surname; private readonly INameSource _nameSource; public Speaker(string firstName, string surname, INameSource nameSource) { this._firstName = firstName; this._surname = surname; this._nameSource = nameSource; } public string Introduce() { string name = _nameSource.CreateName(_firstName, _surname); return string.Format("Hi, my name is {0}", name)
新聞熱點
疑難解答