領域驅動設計之單元測試最佳實踐 二

2022-01-13 07:03:49 字數 2865 閱讀 6407

介紹完了ddd案例,我們終於可以進入主題了,本方案的測試**基於xunit編寫,斷言元件採用了fluentassertions,類似的元件還有shouldly。另外本案例使用了code contracts for .net,如果不安裝此外掛程式,可能有個別測試不能正確pass。

為了實現目標中的第二點:"盡量不mock,包括資料庫讀取部分」,我嘗試過3種方案:

1、測試**連線真實資料庫,只需要將測試資料庫配置到測試專案中的web.config中,即可達到這一目標。但是該方案畢竟存在很多缺點,如:需要將測試庫和正式庫的更改保持同步,單元測試不利於整合在ci中,不利於團隊協作等。

2、使用sql lite,但是由於sql lite本身不支援一些linq表示式如:skip,另外還有一些功能也無法跟sql server保持一致,最終放棄該方案。

3、使用測試元件effort,可以很好的配合entity framework使用,由於effort內部使用了關係型記憶體資料庫nmemory,所以非常適合執行單元測試。

當然我還是非常期待微軟能夠編寫基於ef的單元測試元件。

我在《我眼中的領域驅動設計》一文中提到:不要使用資料庫獨有的技術,如儲存過程和觸發器等。一方面這些邏輯都應該是domain邏輯,另一方面一旦使用了這些技術也就意味著我們無法為這些邏輯編寫測試。

一、使用effort

為了能夠在castle中使用基於effort的dbcontext,需要在castle中註冊effort:

public class fakedbcontextinstaller:iwindsorinstaller

}

二、為測試編寫場景

為了復用測試資料,我們需要編寫場景(scenario),下面的檔案組織結構描述了這一意圖:

以使用者註冊為例,設計registeruserscenario:

public class registeruserscenario : scenariobase

public guid id

public registeruserscenario(iwindsorcontainer container):base(container);}

public override void execute()

}

場景總是提供了正確的資料,執行這樣的場景總是能夠得到正確的結果:

[fact]

public void when_registeruserwithvaliddata_should_createuser()

測試的方法名很重要,我們在讀完這個方法名之後就知道該測試是在幹嘛。

為了得到失敗的結果,我們需要重寫scenario中的資料,比如下面的測試:

[fact]

public void when_registeruserwithemptyname_should_throwexception()

};//act

scenario.invoking(s => s.execute()).shouldthrow("invalid username");

}

三、基於之前的場景編寫新的場景,從而達到復用資料的目的

例如我們需要編寫「使用者登入」的測試,首先需要編寫loginscenario

public class loginscenario:scenariobase

public string password

public bool login

public guid id

public loginscenario(iwindsorcontainer container) : base(container)

public override void execute()

}

在這個場景的建構函式中我們又執行了registerscenario,從而達到重複利用資料的目的。

為「使用者登入」編寫測試:

public class userlogintests:testbase

;//act

}[fact]

public void when_loginwithwrongpassword_should_returnfalse()

;//act

loginscenario.execute();

//assert

loginscenario.login.should().befalse();

} [fact]

public void when_loginwithcorrectpassword_should_returntrue()

}

我們總是需要為新的業務邏輯編寫新的場景,而新的場景總是基於之前編寫好的場景,整個系統的任何功能都可以用真實的測試**來覆蓋。

由於我們在測試基類中為每個測試都開啟了單獨的scope,每乙個測試結束都會dispose資料庫。所以每乙個測試無論執行多少遍都是相同的效果。缺點是這些測試不能並行執行,xunit預設以不同的測試類為單位並行執行,我們通過在測試類上新增相同的[collection("integrationtests")]標籤,從而禁用xunit的並行執行能力。

採用該方案覆蓋完畢單元測試的系統,開發者每次提交**並保證所有單元測是都是「passed」,開發者每一次**提交都會信心滿滿。

高質量的單元測試不但能夠確保系統的平穩執行,更是一種有效的文件,當你讀完每乙個場景的測試用例,你基本就能夠對該業務非常熟悉了。

接近真實的單元測試還可以省去你debug的時間,只要你編寫的測試通過,基本就可以確保後台**的可靠性。另外你可以在任何時候從這些測試**中debug進去,相比從前端介面debug**能夠節省不少時間,一勞永逸。

更多具體細節請檢視原始碼:

ABAP單元測試最佳實踐

本文包含了我在開發專案中經歷過的實用的abap單元測試指導方針。我把它們安排成為問答的風格,歡迎任何人新增更多的q a s,以完成這個列表。method test 2 lief 2 pal.test assignment of deliveries to handling units set cod...

軟體單元測試(Unit Test )最佳實踐

我們回顧幾種單元測試的最佳實踐。首先,tdd 是非常有價值的實踐。在所有現有的開發方法中,tdd 可能是多年來根本上改進開發且投資成本最小的一種。每個 qa 工程師都會告訴您,開發人員在沒有相應的測試前不會寫出成功的軟體。有了 tdd,實踐是在實現前編寫測試,並且理想情況是,編寫的測試可以成為無需人...

測試驅動開發之基礎 單元測試

學習測試驅動開發之前,應當正確理解一下單元測試的概念,學習單元測試之後可以清楚的知道所謂的單元為單一職責的乙個方法即乙個方法只做一件事情,這也符合物件導向的單一職責的原則。因此單元測試的概念可以籠統的理解為 針對乙個工作單元設計的測試。單元測試有各種不同的編寫方式,但所有單元測試有些共同的特徵 1....