测试驱动开发上的五大错误(2)

发表于:2014-05-27来源:外刊It评论作者:不详点击数: 标签:测试驱动开发
15. { 16. return null; 17. } 18. 19. public IEnumerable GetProducts() 20. { 21. throw new NotImplementedException(); 22. } 23.} 在这个例子中,我们为每个测试都做了一个独立的Reposi

  15. {

  16. return null;

  17. }

  18.

  19. public IEnumerable GetProducts()

  20. {

  21. throw new NotImplementedException();

  22. }

  23.}

  在这个例子中,我们为每个测试都做了一个独立的Repository。但我们也可在一个Repository上添加额外的逻辑,例如:

  01.public class StubProductRepository : IProductRepository

  02.{

  03. public Product GetByID(string id)

  04. {

  05. if (id == "spr-product")

  06. {

  07. return new Product()

  08. {

  09. ID = "spr-product",

  10. Name = "Nice Product"

  11. };

  12. }

  13.

  14. return null;

  15. }

  16.

  17. public IEnumerable GetProducts()

  18. {

  19. throw new NotImplementedException();

  20. }

  21.}

  在第一种方法里,我们写了两个不同的IProductRepository模拟方法,而在第二种方法里,我们的逻辑变得有些复杂。如果我们在这些逻辑中犯了错,那我们的测试就没法得到正确的结果,这又为我们的调试增加了额外的负担,我们需要找到是业务代码出来错还是测试代码不正确。

  你也许还会质疑这些模拟代码中的这个没有任何用处的 GetProducts()方法,它是干什么的?因为IProductRepository接口里有这个方法,我们不得不加入这个方法以让程序能编译通过——尽管在我们的测试中这个方法根本不是我们考虑到对象。

  使用这样的测试方法,我们不得不写出大量的临时模拟类,这无疑会让我们在维护时愈加头痛。这种时候,使用一个模拟框架,比如JustMock,将会节省我们大量的工作。

  让我们重新看一下之前的这个测试例子,这次我们将使用一个模拟框架:

  01.[TestMethod]

  02.public void GetProductWithValidIDReturnsProduct()

  03.{

  04. // Arrange

  05. IProductRepository productRepository = Mock.Create();

  06. Mock.Arrange(() => productRepository.GetByID("spr-product")).Returns(new Product());

  07. ProductService productService = new ProductService(productRepository);

  08.

  09. // Act

  10. Product product = productService.GetByID("spr-product");

  11.

  12. // Assert

  13. Assert.IsNotNull(product);

  14.}

  15.

  16.[TestMethod]

  17.public void GetProductWithInValidIDThrowsException()

  18.{

  19. // Arrange

  20. IProductRepository productRepository = Mock.Create();

  21. ProductService productService = new ProductService(productRepository);

  22.

  23. // Act & Assert

  24. Assert.Throws(() => productService.GetByID("invalid-id"));

  25.}

  有没有注意到我们写的代码的减少量?在这个例子中代码量减少49%,更准确的说,使用模拟框架测试时代码是28行,而没有使用时是57行。我们还看到了整个测试方法变得可读性更强了!

  2、测试代码组织的太松散

  模拟框架让我们在模拟测试中的生成某个依赖类的工作变得非常简单,但有时候太轻易实现也容易产生坏处。为了说明这个观点,请观察下面两个单元测试,看看那一个容易理解。这两个测试程序是测试一个相同的功能:

  Test #1

  01.TestMethod]

  02.public void InitializeWithValidProductIDReturnsView()

  03.{

  04. // Arrange

  05. IProductView productView = Mock.Create();

  06. Mock.Arrange(() => productView.ProductID).Returns("spr-product");

  07.

  08. IProductService productService = Mock.Create();

  09. Mock.Arrange(() => productService.GetByID("spr-product")).Returns(new Product()).OccursOnce();

  10.

  11. INavigationService navigationService = Mock.Create();

  12. Mock.Arrange(() => navigationService.GoTo("/not-found"));

  13.

  14. IBasketService basketService = Mock.Create();

  15. Mock.Arrange(() => basketService.ProductExists("spr-product")).Returns(true);

  16.

  17. var productPresenter = new ProductPresenter(

原文转自:http://www.vaikan.com/top-5-tdd-mistakes/