我曾經寫過很多的糟糕的單元測試程序。很多。但我堅持著寫,現在我已經喜歡上了些單元測試。我編寫單元測試的速度越來越快,當開發完程序,我現在有更多的信心相信它們能按照設計的預期來運行。我不希望我的程序里有bug,很多次,單元測試在很多弱智的小bug上挽救了我。如果我能這樣并帶來好處,我相信所有的人都應該寫單元測試!
作為一個自由職業者,我經常有機會能看到各種不同的公司內部是如何做開發工作的,我經常吃驚于如此多的公司仍然沒有使用測試驅動開發(TDD)。當我問“為什么”,回答通常是歸咎于下面的一個或多個常見的錯誤做法,這些錯誤是我在實施驅動測試開發中經常遇到的。這樣的錯誤很容易犯,我也是受害者。我曾合作過的很多公司因為這些錯誤做法而放棄了測試驅動開發,他們會持有這樣一種觀點:驅動測試開發“增加了不必要的代碼維護量”,或“把時間浪費在寫測試上是不值得的”。
人們會很合理的推斷出這樣的結論:
寫了單元測試但沒有起到任何作用,那還不如不寫。
但根據我的經驗,我可以很有信心的說:
單元測試能讓我的開發更有效率,讓我的代碼更有保障。
帶著這樣的認識,下面讓我們看看一些我遇到過/犯過的最常見的在測試驅動開發中的錯誤做法,以及我從中學到的教訓。
1、不使用模擬框架
我在驅動測試開發上學到第一件事情就是應該在獨立的環境中進行測試。這意味著我們需要對測試中所需要的外部依賴條件進行模擬,偽造,或者進行短路,讓測試的過程不依賴外部條件。
假設我們要測試下面這個類中的GetByID方法:
01.public class ProductService : IProductService
02.{
03. private readonly IProductRepository _productRepository;
04.
05. public ProductService(IProductRepository productRepository)
06. {
07. this._productRepository = productRepository;
08. }
09.
10. public Product GetByID(string id)
11. {
12. Product product = _productRepository.GetByID(id);
13.
14. if (product == null)
15. {
16. throw new ProductNotFoundException();
17. }
18.
19. return product;
20. }
21.}
為了讓測試能夠進行,我們需要寫一個IProductRepository的臨時模擬代碼,這樣ProductService.GetByID就能在獨立的環境中運行。模擬出的IProductRepository臨時接口應該是下面這樣:
01.[TestMethod]
02.public void GetProductWithValidIDReturnsProduct()
03.{
04. // Arrange
05. IProductRepository productRepository = new StubProductRepository();
06. ProductService productService = new ProductService(productRepository);
07.
08. // Act
09. Product product = productService.GetByID("spr-product");
10.
11. // Assert
12. Assert.IsNotNull(product);
13.}
14.
15.public class StubProductRepository : IProductRepository
16.{
17. public Product GetByID(string id)
18. {
19. return new Product()
20. {
21. ID = "spr-product",
22. Name = "Nice Product"
23. };
24. }
25.
26. public IEnumerable GetProducts()
27. {
28. throw new NotImplementedException();
29. }
30.}
現在讓我們用一個無效的產品ID來測試這個方法的報錯效果。
01.[TestMethod]
02.public void GetProductWithInValidIDThrowsException()
03.{
04. // Arrange
05. IProductRepository productRepository = new StubNullProductRepository();
06. ProductService productService = new ProductService(productRepository);
07.
08. // Act & Assert
09. Assert.Throws(() => productService.GetByID("invalid-id"));
10.}
11.
12.public class StubNullProductRepository : IProductRepository
13.{
14. public Product GetByID(string id)
原文轉自:http://www.vaikan.com/top-5-tdd-mistakes/