我刚刚开始学习单元测试和模拟。我花了一整天时间阅读不同的教程,试图找到最适合练习的教程。我已经确定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()
时将其拉出来吗?也许我不能正确理解嘲笑的想法?
答案 0 :(得分:2)
为什么我的模拟设置为空?
因为您没有配置( setup ),所以当您向其插入数据时,它会采取任何操作。创建模拟时,它会失去&#34;原始类中存在的所有逻辑并标记为virtual
。 Moq只是覆盖了这些方法,以便以后可以将它们配置为执行任何操作(返回值,抛出等)。
当然,您可以设置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 &#34;调用模拟方法时调用此代码&#34; 。
您还可以尝试使用CallBase参数来阻止覆盖Add
。但是,这可能不起作用,因为您的DbSet不知道list
存在,您很可能不得不更严重地配置它。
最后一点,一旦您的实验结晶,您应该意识到配置Add
不是必要的,因为您不会通过GetAllBlogs
方法检查是否添加了博客而是通过对模拟本身的验证:
mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
答案 1 :(得分:0)
您是否尝试使用CallBase属性?
mockSet.CallBase = true;