为什么我的对象嘲笑失败?

时间:2009-01-29 06:04:37

标签: c# moq

我正在使用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;

6 个答案:

答案 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. 不要模拟被测系统
  2. 模拟依赖项
  3. 将您的断言基于被测系统的职责,而不是依赖

答案 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并非无足轻重。

在你的情况下,你假设IProductRepositoryProducts,我预计会Products。我假设ProductRepositort默认情况下未添加到Product(根据我之前的帖子),我还假设为了引用productId,它必须有{{1} (顺便说一句,你应该通过mutator进行变异)。

如果您想从Product中检索ProductRepository,我认为您应该通过模拟框架向其添加Productthe moq site举例说明在页面顶部注册和验证的权利),并确保ProductRepostoryProduct添加了一个默认标识符(推荐)或添加ProductProductRepository添加到{{1}}之前的{{1}}标识符。