我正在使用Moq,似乎无法让我的单元测试传递看似简单的模拟场景。
Product p = new Product();
var rep = new Mock<IProductRepository>();
rep.Expect(x => x.GetProductById(1)).Returns(p);
p = rep.Object.GetProductById(1);
Assert.AreEqual(1, p.ProductId); //Assert.AreEqual failed. Expected:<1>. Actual:<0>.
关于我做错的任何指示?我的单元测试报告: -
Assert.AreEqual失败。预期:其中-1。实际:&℃,GT;
答案 0 :(得分:4)
我认为你错过了如何在测试中使用模拟对象......
您正在做的是在同时测试它的同时模拟ProductRepository对象。这没有多大意义;你不应该嘲笑你正在测试的对象。
假设您有一个要测试的类,ProductService,它依赖于另一个类IProductRepository。在测试ProductService时,您需要模拟依赖,IProductRepository。这允许您完全控制被测试类与其(模拟)依赖项之间的交互。
当您这样做时,您的断言将基于您对待测试的类ProductService的期望。例如,如果使用productService.GetProductById(1)
之类的东西调用ProductService,您将期望ProductService对象使用正确的参数调用其IProductRepository方法一次:repository.GetProductById(1)
。您可能还希望ProductService返回IProductRepository为其提供的同一对象。无论存储库做什么,这都是ProductService的责任。
话虽如此,您的测试可能看起来更像这样:
//Arrange
int testId = 1;
var fakeProduct = new Product{ Id = testId };
var mockRepo = new Mock<IRepository>();
var productService = new ProductService(mockRepo);
mockRepo.Expect(repo => repo.GetProductById(testId)).Returns(fakeProduct);
//Act
Product returnedProduct = productService.GetProductById(testId);
//Assert
mockRepo.Verify(repo => repo.GetProductById(testId), TimesExactly(1));
Assert.AreEqual(returnedProduct.Id, fakeProduct.Id);
我的语法可能已关闭,但希望样本能够获得几点:
答案 1 :(得分:3)
您正在创建一个对象,将该对象设置为方法的返回值,然后检查模拟是否正在改变该对象,这是模拟不打算做的事情您基本上是这样做的:
Product getProductById(Product p) { return p; }
...
Product p = new Product();
Assert.AreEqual(1, getProductById(p).ProductID );
创建新产品时:
Product p = new Product();
我猜默认的ProductID是0,所以句子是:
getProductById(p).ProductID
显然会返回0。
我也很嘲笑这里,但我不明白你的观点。你想测试什么? Product类,ProductRepository或它们之间的交互?这是第一个要考虑的事情。
答案 2 :(得分:1)
我正在查看你的代码,它看起来并不像你知道你想要实现的目标。在编写任何测试之前,请始终提出以下问题:“我想在这里证明什么?”您将创建您创建的产品,但其ID将是默认值(0);这是预期的行为,即模拟框架工作正常。
在测试中使用测试双打(模拟,存根,假货,间谍等)为被测试的班级提供合作者。他们将有趣的值输入或接受来自被测试类的调用 - 它们不是测试的焦点。
您的测试当前正在对测试double本身返回的值进行断言。根据我的理解,你要求提供一个不合时宜的代码片段(片段本身只是一个例子,而不是测试本身),或者它是真正的测试代码毫无意义。
我见过很多人用嘲弄框架打结自己。 That's why, to begin with, I would recommend hand-writing your own test doubles。这有助于您了解对象之间的交互,并形成您希望测试的更清晰的图像。再次,这可以追溯到“我想在这里证明什么?”的问题。
一旦你理解了如何使用手动测试双打来实现目标,你就可以毕业于模拟框架。
答案 3 :(得分:0)
在设置期望值时,您将产品实例p用作“预期值” - 返回(p) 然后使用相同的引用来存储实际调用的返回值。
对于Assert失败,新的Product()是否将其ProductId初始化为1.看起来它被设置为默认值0 - 因此错误。
我认为Mock框架正在运行。
答案 4 :(得分:0)
我会先看看Gishu的观点:产品初始化。除非IProductRepository的默认行为是返回ProductId引用的产品'1',否则您的测试将失败。
而且,我可以补充一下,这种失败似乎是明智的行为。我认为你希望ProductRepository在初始化时为空。
答案 5 :(得分:0)
并不完全忽略这一点,Mocking并非无足轻重。
在你的情况下,你假设IProductRepository
有Products
,我预计会Products
。我假设ProductRepositort
默认情况下未添加到Product
(根据我之前的帖子),我还假设为了引用productId
,它必须有{{1} (顺便说一句,你应该通过mutator进行变异)。
如果您想从Product
中检索ProductRepository
,我认为您应该通过模拟框架向其添加Product
(the moq site举例说明在页面顶部注册和验证的权利),并确保ProductRepostory
为Product
添加了一个默认标识符(不推荐)或添加Product
将ProductRepository
添加到{{1}}之前的{{1}}标识符。