C# 为什么我的对象模拟失败了?

C# 为什么我的对象模拟失败了?,c#,moq,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 faile

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


在设置expect-Returns p时,您将产品实例p作为“期望值”使用一次 然后使用相同的引用存储实际调用的返回值

至于断言失败,新产品是否将其ProductId初始化为1。似乎它被设置为默认值0-因此出现错误


我认为模拟框架正在工作

我支持Gishu re的观点:产品初始化。除非IPProductRepository的默认行为是返回ProductId为“1”的产品,否则测试将失败


我还要补充一点,这种失败似乎是明智的行为。我认为您希望ProductRepository在初始化时为空。

并不是完全没有抓住要点,模拟并不是小事

在您的情况下,您有一个IPProductRepository,我想,它应该会保存产品。根据我之前的文章,我假设默认情况下没有将产品添加到ProductRepositor,并且我还假设为了引用产品,它必须有一个productId,顺便说一下,您确实应该通过


如果您想从ProductRepository中检索产品,我认为您应该通过mock framework向其添加产品。在页面顶部给出了注册和验证的示例,并确保ProductRepostory为添加到其中的产品提供一个不推荐的默认标识符,或者在将其添加到ProductRepository之前为产品添加一个标识符。

您正在创建一个对象,将该对象设置为方法的返回值,然后检查模拟是否正在更改该对象,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;这是预期的行为,即模拟框架工作正常

双重测试模拟、存根、赝品、间谍等在测试中用于为被测类提供协作者。它们将有趣的值输入到被测类中或接收来自被测类的调用-它们不是测试的焦点

您的测试当前正在断言测试本身返回的值。据我所知,要么你是在请求上下文之外的代码片段的帮助——代码片段本身只是一个示例,而不是测试本身,要么是毫无意义的真实测试代码

我见过许多人用嘲弄的框架把自己绑在一起。这有助于您理解对象之间的交互,并对您希望测试的内容形成更清晰的了解。这又回到了我要证明什么的问题,这里


一旦您了解了如何使用手动翻滚测试来实现一个目标,您就可以开始模拟框架了。

我认为您忽略了如何在测试中使用模拟对象的要点

您所做的是模拟ProductRepository对象,同时对其进行测试。这没有多大意义;你不应该模仿你正在测试的对象

假设您有一个要测试的类ProductService,它依赖于另一个类IPProductRepository。当您测试ProductService时,您需要模拟依赖项IPProductRepository。这允许您完全控制被测试类与其模拟依赖项之间的交互

当您这样做时,您的断言将基于您期望被测试的类ProductService所做的事情。例如,如果使用类似ProductService.GetProductById1的内容调用ProductService,则ProductService对象将使用正确的参数调用其IPProductRepository方法一次:repository.GetProductById1。您还可能期望ProductService返回IPProductRepository提供给它的相同对象。无论存储库做什么,这都是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);
我的语法可能不正确,但希望示例能够理解以下几点:

不要模仿正在测试的系统 模拟依赖关系 垒溜溜球 ur对被测系统责任的断言,而不是依赖性
IPProductRepository没有默认行为——它是一个接口;模拟框架只是按照它所说的去做。测试通过的唯一方法是,使用默认ctor创建的产品的ID始终为1而不是0。即使这样,它也不会测试任何有价值的东西。