時光飛逝後的現在,一兩年時間過去了,這段時間內做的專案"測試工作"算是開發中很重要的公事,最近再回頭看網路上IN91的教學文章特別有感觸以及體會其中的奧妙,因此想要寫下些紀錄。
在TDD(Test-Driven Development)中分成了幾個步驟:
1. ATDD & BDD
2. TDD
3. Testing
4. Refactoring
在3項中Test還分成"驗收測試"、"整合測試"及"單元測試",在這一篇中主要講的是Testing中的單元測試中的Stub及Mock
Unit Test是對單一目標物件做測試,基本原則如下:
1. 一個測試案例只能有一個方法
2. 最小的測試單位
3. 不與外部(包含檔案、資料庫、網路、服務、物件或類別)直接相依
4. 不具備邏輯
5. 測試案例之間相依性為0
另外還有Stub, Mocks 及Fake
Stub - 用來驗證受測目標物件的回傳值 以及 驗證受測目標狀態改變
Mock - 則可用來驗證測試目標物件與其相依物件互動
Fake - 在測試中若需要用到.Net Framework原生套件,則可以用Fake來建立一個假的來用
用上一篇<Interface使用>來做一下練習。在這有一個Interface叫INewThings,他的實作則在Company裡。
public interface INewThings { string CreateNewMember(PersonInfo person); }; public class PersonInfo { public string Name { get; set; } public int age { get; set; } } public class Company : INewThings { string INewThings.CreateNewMember(PersonInfo PI) { if(PI.age <18) { return "too young to work"; } else if(PI.age >65) { return "too old to work"; } else { return PI.Name + "-" + PI.age; } } public string humanresource(INewThings NewThings, PersonInfo Newguy) { return NewThings.CreateNewMember(Newguy); } }
接著建立測試專案並撰寫測試案例,依照這程式它共有三個狀態,基於單元測試原則一次只驗證一件事情,因此TestMethod會有三個。
在Visual Studio中要使用Stub Mocks需先透過NuGet安裝,在測試專案上按右鍵選擇管理NuGet套件,並搜尋RhinoMocks下載。
接著撰寫測試程式
[TestMethod] public void TestAgeisInRange() { //arrange INewThings stubNewThing = MockRepository.GenerateStub< INewThings >(); stubNewThing.Stub(x => x.CreateNewMember(Arg.Is.Anything)).Return("Frank-27"); Company cp = new Company(); PersonInfo PI = new PersonInfo(); PI.Name = "Frank"; PI.age = 27; //act var actualWork = cp.humanresource(stubNewThing, PI); Assert.AreEqual("Frank-27", actualWork); } [TestMethod] public void TestAgeLessThan18() { //arrange INewThings stubNewThing = MockRepository.GenerateStub < INewThings >();< INewThings >(); stubNewThing.Stub(x => x.CreateNewMember(Arg .Is.Anything)).Return("too young to work"); Company cp = new Company(); PersonInfo PI = new PersonInfo(); PI.Name = "Brown"; PI.age = 10; //act var actualWork = cp.humanresource(stubNewThing, PI); Assert.AreEqual("too young to work", actualWork); } [TestMethod] public void TestAgeGreatThan80() { //arrange INewThings stubNewThing = MockRepository.GenerateStub
stubNewThing.Stub(x => x.CreateNewMember(Arg .Is.Anything)).Return("too old to work"); Company cp = new Company(); PersonInfo PI = new PersonInfo(); PI.Name = "Chen"; PI.age = 85; //act var actualWork = cp.humanresource(stubNewThing, PI); Assert.AreEqual("too old to work", actualWork); }
在程式中可以看到因為humanresource這個方法其中一個參數是Interface INewThings,因此透過MockRepository.GenerateStub< INewThings >() 來生成一個獨立的物件提供測試使用,透過Mock產生取代物件後,還要透過.Return來設定預期回傳的值。
以上是Stub的寫法,接著以同樣範例來以Mock方式來撰寫
[TestMethod] public void TestViaMocks() { MockRepository mock = new MockRepository(); INewThings stubNewThings = mock.StrictMock(); Company cp = new Company(); List People = new List (); PersonInfo person1 = new PersonInfo(); person1.Name = "Frank"; person1.age = 27; People.Add(person1); PersonInfo person2 = new PersonInfo(); person2.Name = "Brown"; person2.age = 12; People.Add(person2); PersonInfo person3 = new PersonInfo(); person3.Name = "Chen"; person3.age = 89; People.Add(person3); using (mock.Record()) { cp.humanresource(stubNewThings, People[0]); LastCall .Return("Frank-27"); cp.humanresource(stubNewThings, People[1]); LastCall .Return("too young to work"); cp.humanresource(stubNewThings, People[2]); LastCall .Return("too old to work"); } using (mock.Playback()) { var target1 = cp.humanresource(stubNewThings, People[0]); var target2 = cp.humanresource(stubNewThings, People[1]); var target3 = cp.humanresource(stubNewThings, People[2]); } }
結論:
根據In91大大的經驗
使用 stub 的比例大概是8~9成,使用mock的比例大概僅1~2成。而 fake 的方式,則用在特例,例如靜態方法跟 .net framework 原生組件。而本身實務經驗上也很少用到這三個,也許打滾的還不夠久,但先熟悉起來未來也許會用到的!另外最近還摸了BDD、Selenium及重構,在另外做個紀錄。
資料參考
In91 - [30天快速上手TDD]
沒有留言:
張貼留言