【问题标题】:How to mock the functionality of the test class itself using Moq in ASP.NET MVC unit test如何在 ASP.NET MVC 单元测试中使用 Moq 模拟测试类本身的功能
【发布时间】:2016-06-28 12:55:15
【问题描述】:

我正在开发一个 ASP.NET MVC 项目。在我的项目中,我正在做单元测试。我使用 Moq 进行单元测试。但是我在测试依赖于另一个函数的模型类的方法时遇到了问题。

这是我要测试的模型类示例

public class ItemRepo:IItemRepo
{
    private DbContext context = new DbContext();

    public IEnumerable<Item> GetItems()
    {
        return context.items;
    }

    public string GenerateItemCode()
    {
       Item item = this.GetItems.OrderByDescending(x=>x.Id).FirstOrDefault();
       //do something
       return itemCode;
    }
}

我想测试 GenerateItemCode 方法

    [TestClass]
    public class ItemRepoTests
    {
        [TestMethod]
        public void GenerateItemCode_IncreaseDigit()
        {
            Item[] items = new Item[]{
                new Item{
                    ItemCode = "DN999934"
                }
            };
            ItemRepo itemRepo = new ItemRepo();
            //I want to mock GetItems method here
        }
    }

我在测试代码中注释了我想模拟的内容。我该如何模拟该方法?如何对模拟依赖函数的方法进行单元测试?

【问题讨论】:

  • items 是如何引入模型的。它是在内部创建还是注入到类中。
  • 这个问题的GetItems方法里面的代码比较重要,请您添加它,这样将向我们展示您是如何从数据库中获取数据的。例如,如果它使用的是 EF DbContext,那么我们可以建议您注入一个模拟 DbContext 来运行单元测试。
  • GetItems 方法只是返回 context.Items
  • 请提供minimal reproducible example,以便重现您的问题并提出解决方案。在 cmets 中为您提供点点滴滴。例如,在您的原始问题中没有提到任何上下文。尝试使用相关详细信息更新您的问题。
  • 是的,我编辑了这个问题。

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


【解决方案1】:

您在评论中提到您从上下文类中获取项目,这是您需要模拟的类。

周围有很多文章解释了如何模拟 DbContext,Google 搜索的热门结果How to mock DbContext and DbSet with Moq for unit testing?

[TestClass]
public class ItemRepoTests
{
    [TestMethod]
    public void GenerateItemCode_IncreaseDigit()
    {
       Item[] items = new Item[]{
               new Item{
                          ItemCode = "DN999934"
                       }
        };

       var mockContext = new Mock<YourContext>();
       // Code to inject items into mock context

       // You may have to implement the context injection into your ItemRepo 
       // class if you do not already have it

       ItemRepo itemRepo = new ItemRepo(mockContext.Object);

       var result = itemRepo.GenerateItemCode();

      // Code to check result is correct
   }
}

因此,基本上,您在单元测试中创建一个模拟 DbContext 并传入一个虚假列表或类似的项目数据。然后你将这个模拟注入你正在测试的类中,运行测试,如果你的代码一切都很好,那么正确的结果将在另一端吐出

【讨论】:

  • 我删除了我以前的 cmets。问题是它模拟了 IDbSet。但我使用 DbSet 的上下文。我不能用 DbSet 模拟。所以我在我的上下文类中为实体更改了所有 IDbSet。但是当我更改为 IDbSet 时,有些函数将 removeRange 与 Items 一起使用。所以我不能使用 removeRange 到 IDbSet。那么有什么想法吗?
【解决方案2】:

将 GetItems 替换为委托:

 public class ItemRepo : IItemRepo
 {
     public Func<System.Collections.Generic.IEnumerable<Item>> Items = () => 
       {  // all your orignal code 
          return items;
       };  

      public string GenerateItemCode()
      {
         Item item = Items().OrderByDescending(x => x.Id).FirstOrDefault();
         //do something
         return itemCode;
      }
 }

在测试代码中替换你的委托:

 public class ItemRepoTests
 {
     [TestMethod]
     public void GenerateItemCode_IncreaseDigit()
     {
        Item[] items = new Item[]{
            new Item{
                ItemCode = "DN999934"
            }
        };
        ItemRepo itemRepo = new ItemRepo();
        itemRepo.Items = () => items;
        //I want to mock GetItems method here

         itemRepo.GenerateItemCode()
     }
  }

伪代码不是testet,仅用于描述想法

【讨论】:

  • 如果我使用委托,我无法从委托函数中访问测试类的其他属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
相关资源
最近更新 更多