【问题标题】:Unit testing Put method within Microsoft EF Web API 2Microsoft EF Web API 2 中的单元测试 Put 方法
【发布时间】:2014-08-15 23:27:38
【问题描述】:

我正在关注一篇文章 - http://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2 - 对 Web API 2 控制器进行单元测试。

在那里,作者测试put方法如下:

   //....

   Product GetDemoProduct()
    {
        return new Product() { Id = 3, Name = "Demo name", Price = 5 };
    }

   [TestMethod]
    public void PutProduct_ShouldReturnStatusCode()
    {
        var controller = new ProductController(new TestStoreAppContext());

        //** Edited myself from original:
        //** var item = GetDemoProduct(); 
        var updatedItem = new Product(){ Id = 3, Name = "Demo name", Price = 6 };

        var result = controller.PutProduct(3, updatedItem) as StatusCodeResult;
        Assert.IsNotNull(result);
        Assert.IsInstanceOfType(result, typeof(StatusCodeResult));
        Assert.AreEqual(HttpStatusCode.NoContent, result.StatusCode);
        //** Added a new assertion.
        Assert.AreEqual(updatedItem.Price, GetDemoProduct().Price);
    }

哪些测试将方法放入控制器中

public IHttpActionResult PutProduct(int id, Product product)
{

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (id != product.Id)
    {
        return BadRequest();
    }

    //db.Entry(product).State = EntityState.Modified;
    db.MarkAsModified(product);

     try
     {
        db.SaveChanges();
     }

     //....
     return StatusCode(HttpStatusCode.NoContent);     
}

为了验证更新确实发生了,我在原始测试方法中添加了几行,在块中用 ** 表示。

嗯,模拟数据项最终没有得到更新。价格与更新前的产品价格保持一致。

db.SaveChanges(); 怎么来的?不行吗?

【问题讨论】:

  • var updatedItem = new item(){ Id = 你在某处有一个类叫item
  • 对不起,那是复制粘贴的错字。它应该是 Product 实例。
  • 因此,如果没有断言 updatedItem == pre-updatedItem,示例单元测试就可以了。它返回 NoCoentent 状态代码。只是它实际上并没有更新模拟数据库。

标签: c# asp.net entity-framework unit-testing asp.net-web-api2


【解决方案1】:

刚刚发现作者故意在测试上下文中更改了 saveChanges 方法。

public class TestStoreAppContext : IStoreAppContext 
{
    public TestStoreAppContext()
    {
        this.Products = new TestProductDbSet();
    }

    public DbSet<Product> Products { get; set; }

    public int SaveChanges()
    {
        return 0;
    }
    //....

现在需要想想他为什么要更改 saveChanges() 方法。

谢谢大家。

【讨论】:

    【解决方案2】:

    1) 您使用 6 的价格初始化 updatedItem(您的参考模拟的价格为 5),将其传递给 PutProduct 方法,但该方法从不修改价格。我不确定你期望发生什么。如果不修改价格,显然是一样的。你打算在某个地方做product.Price = 5 吗?否则它将与您的参考对象不匹配,并且价格仍为 6。

    2) 在 WebAPI 方法的输入参数中使用断言是一个糟糕的测试。 updatedItem 实际上将来自绑定到参数的 HTTP 请求。您是否修改该实体的属性对方法之外的任何内容都没有什么影响,因为没有其他任何东西可以引用它。您在测试用例中引用它只是因为这就是我们伪造 HTTP 请求的方式。我们跳过了整个模型绑定步骤。实际上,没有人会引用它,并且在调用完成后它将完全无关紧要。你应该只关心两件事,数据库的状态和返回。你已经完成了返回测试,你只是在测试数据库修改时偏离了基础。首先强调一下为什么这个测试不好:

    考虑一下我是否忘记调用 SaveChanges:

    public IHttpActionResult PutProduct(int id, Product product)
    {
         product.Price = 5;
         return StatusCode(HttpStatusCode.NoContent);     
    }
    

    您的断言会成功,因为我的修改会导致 updatedProduct 的价格为 5,从而匹配参考对象:

    Assert.AreEqual(updatedItem.Price, GetDemoProduct().Price);

    但是,由于程序员忘记调用 SaveChanges,PutProduct 的实现是错误的,但是您的测试用例错过了这一点。您确实需要通过相同的上下文查询数据库,以查看更改是否真正持久化。您可以使用实际的 localdb 执行此操作,如果您将 EF 配置为在每次测试运行时清除 DB,则效果很好,或者更“正确”的方法是使用映射到内存模拟的上下文。这是这里使用的方法:http://www.c-sharpcorner.com/uploadfile/raj1979/unit-testing-in-mvc-4-using-entity-framework/

    如果我们假设您有这样的存储库,它会是这样的:

     [TestMethod]
     public void Create_PutProductInRepository()
     {      
         int testId = 3;     
         ...
         var result = controller.PutProduct(testId, inputProduct) as StatusCodeResult;
         ...
         // This is the kind of testing you really want to be doing.
         //  You want to "assert that the item exists in the database and has the expected value"
         Product productAfterTest = mockrepository.GetProduct(testId);
         Assert.AreEqual(inputProduct.Price, productAfterTest.Price);
     }
    

    显然您正在使用一种没有存储库的方法,因此您必须适应它。重点是,断言需要执行查询以从您的 dbcontext 中检索该项目,以验证它是否存在并保存了预期的修改。我不是制作内存数据库的专家,所以无论你有什么来源,你都必须了解你的方法。但无论哪种方式,您都应该通过转到数据库上下文并检索项目来验证它是否真的被持久化到上下文中来进行断言。

    【讨论】:

    • 谢谢。好吧,我想我需要调查这些。
    猜你喜欢
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 2012-03-18
    • 2023-03-06
    • 1970-01-01
    相关资源
    最近更新 更多