English 中文(简体)
我的对象模拟为什么失败?
原标题:
  • 时间:2009-01-29 06:04:36
  •  标签:

我正在使用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 失败。期望值:<1>。实际值:<0>。

我做错了什么?我的单元测试报告如下:

Assert.AreEqual 失败。期望值:<1>。实际值:<0>。

问题回答

我认为你没有理解如何在测试中使用模拟对象的要点...

你所做的是嘲笑一个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. Don t mock the system under test
  2. Mock the dependencies
  3. Base your assertions on the responsibilities of the system under test, not the dependencies

您正在创建一个对象,将该对象设置为方法的返回值,然后检查mock是否改变该对象,而mock并不打算这样做。您基本上正在做这件事:

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。

我在这里也是模拟新手,但我不明白你的观点。你正在尝试测试什么?产品类、产品存储库还是它们之间的交互?这是第一件要考虑的事情。

我在看你的代码,看起来你并不清楚自己想要实现什么。在编写任何测试之前,都要问自己这个问题:“我在这里试图证明什么?”你创建的产品将被返回,但其ID将是默认值(0),这是预期的行为,即模拟框架正常工作。

测试双倍(模拟、存根、伪造、间谍等)用于测试,为受测试类提供协作的部分。 它们将有趣的值馈送到或从受测试类接收调用 - 它们不是测试的重点。

您的测试当前在测试Double值本身的返回值。据我所知,您要么需要帮助解决上下文不明的代码片段(该片段本身只是一个示例,不是测试本身),要么这是一个无意义的真正测试代码。

我看过许多人在使用模拟框架时陷入困境。因此,我建议首先手写自己的测试替身。这有助于您了解对象之间的交互,并形成更清晰的测试目标。再次强调,“我在这里试图证明什么?”这是一个关键问题。

一旦你理解了如何使用手动制作的测试替身来实现目标,就可以毕业进入模拟框架。

You re using the Product instance p once as the expected value while you are setting up the expect - Returns (p) and then using the same reference to store the return value of the actual call.

关于Assert失败,new Product()是否将其ProductId初始化为1。看起来它正在设置为默认值0-因此出现错误。

我认为Mock框架正在工作。

我赞同Gishu对产品初始化的看法。除非IProductRepository的默认行为是返回一个引用ProductId为1的产品,否则你的测试将失败。

而且,我想补充的是,这个失败似乎是合理的行为。我认为您希望在初始化时ProductRepository是空的。

不完全是错过了重点,嘲讽也不是微不足道的。

在你的情况下,你有一个,我假设它应该持有。我假设不会默认添加到中(根据我之前的帖子),并且我还假设要引用,它必须具有(顺便说一句,你真的应该通过来改变它)。

如果您想从您的产品存储库检索一个 产品,我认为您应该通过模拟框架向其添加 产品moq 网站 在页面顶部给出了注册和验证示例),并确保 ProductRepostory 给其添加的 产品 赋予默认标识符(推荐),或在将其添加到 ProductRepository 之前为 Product 添加标识符。





相关问题
热门标签