【问题标题】:How reliable is Verify() in Moq?起订量中的验证()有多可靠?
【发布时间】:2010-04-08 08:35:52
【问题描述】:

我只是单元测试和 ASP.NET MVC 的新手。我一直在尝试使用 Steve Sanderson 的“Pro ASP.NET MVC Framework”。书中有一段代码:

public class AdminController : Controller
{
 ...

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Product product, HttpPostedFileBase image)
    {
      ...
       productsRepository.SaveProduct(product);

       TempData["message"] = product.Name + " has been saved.";
       return RedirectToAction("Index");
    }
}

他是这样测试的:

[Test]
public void Edit_Action_Saves_Product_To_Repository_And_Redirects_To_Index()
{
    // Arrange
    AdminController controller = new AdminController(mockRepos.Object);

    Product newProduct = new Product();

    // Act
    var result = (RedirectToRouteResult)controller.Edit(newProduct, null);

    // Assert: Saved product to repository and redirected
    mockRepos.Verify(x => x.SaveProduct(newProduct));
    Assert.AreEqual("Index", result.RouteValues["action"]);
}

测试通过。

所以我故意通过添加“productsRepository.DeleteProduct(product);”来破坏代码在“SaveProduct(product);”之后如:

            ...
       productsRepository.SaveProduct(product);
       productsRepository.DeleteProduct(product);
            ...

测试通过了。(即宽恕一个灾难性的 [催眠 + 智能感知] 导致的错字:))

这个测试可以写得更好吗?或者有什么我应该知道的吗?非常感谢。

【问题讨论】:

    标签: asp.net-mvc unit-testing mocking moq verify


    【解决方案1】:

    我认为您可能误解了 .Verify() 方法的目的。

    它验证给定的方法是用期望值调用的。

    Steve 在书的第 187 页上说“注意它如何使用 Moqs .Verify() 方法来确保 AdminController 确实使用正确的参数调用了 DeleteProduct()。

    因此,在您的情况下,测试通过了,因为它只是验证调用而不是功能。

    由于在本书的过程中遵循 TDD,因此添加了

    productsRepository.DeleteProduct(product);
    

    应该先加入测试

    // Assert: Saved product to repository, then deleted and redirected
    mockRepos.Verify(x => x.SaveProduct(newProduct))
    mockRepos.Verify(x => x.DeleteProduct(newProduct));
    Assert.AreEqual("Index", result.RouteValues["action"]);
    

    然后添加到代码中

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Product product, HttpPostedFileBase image)
    {
    
         ...
    productsRepository.SaveProduct(product);
    productsRepository.DeleteProduct(product);
        ...
    

    【讨论】:

      【解决方案2】:

      正如其他人所说,测试通过是因为您的断言:

      mockRepos.Verify(x => x.SaveProduct(newProduct));
      

      已完成。您的代码确实调用了 SaveProduct 方法。

      Mock.Verify() 方法无法验证是否调用了其他方法,这是您期望它执行的操作。

      如果您担心发生一些奇怪的事情(例如在 Save() 之后调用 Delete())并希望通过测试来防止它发生,那么您也必须为该条件添加一个 Verify()。比如:

      mockRepos.Verify(x => x.DeleteProduct(newProduct), Times.Never());
      

      【讨论】:

      • 是的。一些模拟框架支持严格的模拟,可以验证模拟上没有调用其他方法,但这些往往会导致脆弱的测试。
      【解决方案3】:

      您故意“破坏”代码不会破坏测试,因为您在调用Verify()SaveProduct() 之后已经这样做了。我一直觉得 Moq 的 Verify() 非常可靠。

      一些用于更健壮测试的伪代码可能是让您的存储库实现一个接口并拥有一个简单的内存可测试版本

      // Arrange
      
      var repo = SetupTestableRepository() 
      var product = CreateProduct(productId)
      
      // Act 
      repo.SaveProduct(product)
      
      // Assert
      Assert.IsNotNull(repo.Fetch(productId))
      

      善良,

      【讨论】:

        【解决方案4】:

        我们的想法是“编写最简单的有效代码”。这可以帮助您避免做一些愚蠢的事情,例如在增量计数器操作中从磁盘中删除文件。显然,不删除文件更简单。

        【讨论】:

          猜你喜欢
          • 2019-03-03
          • 2012-08-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-26
          相关资源
          最近更新 更多