为什么我的模拟集是空的?

Why is my mock set empty?

我刚刚开始学习单元测试和模拟。我花了一整天的时间阅读不同的教程,试图找到最好的教程来练习。我选择了 Testing with a mocking framework (EF6 onwards),因为它使用的是 EF6(现代),以及似乎非常流行的模拟框架 (Moq)。此外,它非常普通,托管在 MSDN 网站上。一定要体面吧?

我已经完全按照示例中指定的方式设置了一个项目,并且 运行 调试器通过测试示例确保我了解正在发生的事情。我正在进行的测试如下:

[TestClass] 
public class QueryTests 
{ 
    [TestMethod] 
    public void GetAllBlogs_orders_by_name() 
    { 
        var data = new List<Blog> 
        { 
            new Blog { Name = "BBB" }, 
            new Blog { Name = "ZZZ" }, 
            new Blog { Name = "AAA" }, 
        }.AsQueryable(); 

        var mockSet = new Mock<DbSet<Blog>>(); 
        mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); 
        mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); 
        mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); 
        mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); 

        var mockContext = new Mock<BloggingContext>(); 
        mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); 

        var service = new BlogService(mockContext.Object); 
        //test code here
        var blogs = service.GetAllBlogs(); 

        Assert.AreEqual(3, blogs.Count); 
        Assert.AreEqual("AAA", blogs[0].Name); 
        Assert.AreEqual("BBB", blogs[1].Name); 
        Assert.AreEqual("ZZZ", blogs[2].Name); 
    } 
} 

这很简单,我相信我正在理解单元测试和模拟框架。凉爽的!我决定通过在服务实例化之后将 service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet");(来自前面的示例)插入到上面的 TestMethod 中来执行一个实验来验证我自己。

我希望当我走过 var blogs = service.GetAllBlogs(); 时,博客应该包含我的新条目,但它没有。它仅包含 data.

初始化中的 3

这是怎么回事?该代码不应该将博客记录插入 data 对象,从而在调用 GetAllBlogs() 时拉取它吗?也许我没有正确理解模拟的想法?

Why is my mock set empty?

因为您没有配置 (setup) 它在您向其中插入数据时采取任何操作。创建模拟时,它 "loses" 原始 class 上存在并标记为 virtual 的所有逻辑。 Moq 只是重写这些方法,以便稍后您可以配置它们来执行任何操作(return 值、抛出等)。

当然,您可以设置 Add 方法将数据插入回您的 data 博客列表:

var mockSet = new Mock<DbSet<Blog>>();
mockSet.Setup(m => m.Add<It.IsAny<Blog>()).Callback(blog => data.Add(blog));

Add 方法将在您的 DbSet 上调用时(最好通过调用服务)已配置 版本的 Add 将接管并将博客插入您的数据列表(Callback 方法在调用模拟方法时告诉 Moq "invoke this code")。

您也可以尝试使用 CallBase 参数来防止覆盖 Add。但是,这可能行不通,因为您的 DbSet 不知道 list 存在,您很可能需要对其进行更多配置。

最后一点,一旦你的实验具体化,你应该意识到配置 Add 不是必需的,因为你不会检查博客是否是通过 GetAllBlogs 方法添加的,而是通过模拟本身的验证:

mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());

您是否尝试使用 CallBase 属性?

mockSet.CallBase = true;