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

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

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

    [TestMethod] 
    public void GetProductWithValidIDReturnsProduct() 
    { 
        // Arrange 
        IProductRepository productRepository = Mock.Create<IProductRepository>(); 
        Mock.Arrange(() => productRepository.GetByID("spr-product")).Returns(new Product()); 
        ProductService productService = new ProductService(productRepository); 
        
        // Act 
        Product product = productService.GetByID("spr-product"); 
        
        // Assert 
        Assert.IsNotNull(product); 
    } 
        
    [TestMethod] 
    public void GetProductWithInValidIDThrowsException() 
    { 
        // Arrange 
        IProductRepository productRepository = Mock.Create<IProductRepository>(); 
        ProductService productService = new ProductService(productRepository); 
        
        // Act & Assert 
        Assert.Throws<ProductNotFoundException>(() => productService.GetByID("invalid-id")); 
    }

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

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

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

  Test #1

    TestMethod] 
    public void InitializeWithValidProductIDReturnsView() 
    { 
        // Arrange 
        IProductView productView = Mock.Create<IProductView>(); 
        Mock.Arrange(() => productView.ProductID).Returns("spr-product"); 
        
        IProductService productService = Mock.Create<IProductService>(); 
        Mock.Arrange(() => productService.GetByID("spr-product")).Returns(new Product()).OccursOnce(); 
        
        INavigationService navigationService = Mock.Create<INavigationService>(); 
        Mock.Arrange(() => navigationService.GoTo("/not-found")); 
        
        IBasketService basketService = Mock.Create<IBasketService>(); 
        Mock.Arrange(() => basketService.ProductExists("spr-product")).Returns(true); 
            
        var productPresenter = new ProductPresenter( 
                                                productView, 
                                                navigationService, 
                                                productService,  
                                                basketService); 
        
        // Act 
        productPresenter.Initialize(); 
        
        // Assert 
        Assert.IsNotNull(productView.Product); 
        Assert.IsTrue(productView.IsInBasket); 
    }

  Test #2

    [TestMethod] 
    public void InitializeWithValidProductIDReturnsView() 
    { 
        // Arrange    
        var view = Mock.Create<IProductView>(); 
        Mock.Arrange(() => view.ProductID).Returns("spr-product"); 
        
        var mock = new MockProductPresenter(view); 
        
        // Act 
        mock.Presenter.Initialize(); 
        
        // Assert 
        Assert.IsNotNull(mock.Presenter.View.Product); 
        Assert.IsTrue(mock.Presenter.View.IsInBasket); 
    }