I am using the Moq and can't seem to get my unit test to pass on what appears to be a simple mocking scenario.
我正在使用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>.
Any pointers on what I'm doing wrong? My Unit test reports:-
关于我做错了什么的指示?我的单元测试报告: -
Assert.AreEqual failed. Expected:<1>. Actual:<0>.
Assert.AreEqual失败。预期:<1>。实际:<0>。
6 个解决方案
#1
I think you are missing the point of how to use mock objects in tests...
我想你错过了如何在测试中使用模拟对象的观点......
What you are doing is mocking a ProductRepository object while simultaneously testing it. That doesn't make much sense; you should not mock the object you're testing.
您正在做的是在同时测试它的同时模拟ProductRepository对象。这没有多大意义;你不应该嘲笑你正在测试的对象。
Let's say you have a class you want to test, ProductService, and it depends on another class, IProductRepository. When you test the ProductService, you will want to mock the dependency, IProductRepository. This allows you completely control the interaction between the class under test and its (mocked) dependency.
假设您有一个要测试的类,ProductService,它依赖于另一个类IProductRepository。在测试ProductService时,您将需要模拟依赖项IProductRepository。这允许您完全控制被测试类与其(模拟)依赖项之间的交互。
As you do so, your assertions will be based on what you expect the class under test, ProductService, to do. For instance, if you call the ProductService using something like productService.GetProductById(1)
, you will expect the ProductService object to call its IProductRepository method with the correct parameter exactly once: repository.GetProductById(1)
. You may also expect the ProductService to return the same object that the IProductRepository gave it. Regardless of what the repository does, that's the responsibility of the ProductService.
当您这样做时,您的断言将基于您对待测试的类ProductService的期望。例如,如果使用productService.GetProductById(1)之类的东西调用ProductService,您将期望ProductService对象使用正确的参数调用其IProductRepository方法一次:repository.GetProductById(1)。您可能还希望ProductService返回IProductRepository为其提供的同一对象。无论存储库做什么,这都是ProductService的责任。
Having said that, your test may look something more like this:
话虽如此,您的测试可能看起来更像这样:
//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);
My syntax may be off, but hopefully the sample gets across a few points:
我的语法可能已关闭,但希望样本能够获得以下几点:
- Don't mock the system under test
- Mock the dependencies
- Base your assertions on the responsibilities of the system under test, not the dependencies
不要嘲笑被测系统
模拟依赖项
将断言基于被测系统的职责,而不是依赖关系
#2
You are creating an object, setting that object as the return value for a method, and then checking if the mock is altering the object, something that the mock is not intended to do You are basically doing this:
您正在创建一个对象,将该对象设置为方法的返回值,然后检查模拟是否正在改变该对象,这是模拟不打算做的事情您基本上是这样做的:
Product getProductById(Product p) { return p; }
...
Product p = new Product();
Assert.AreEqual(1, getProductById(p).ProductID );
when creating a new Product:
在创建新产品时:
Product p = new Product();
i guess that the default ProductID is 0, so the sentence:
我猜默认的ProductID是0,所以句子:
getProductById(p).ProductID
will obviously return 0.
显然会返回0。
I'm new to mock here too, but I don't see your point. What are you trying to test? The Product class, the ProductRepository, or interactions between them? That is the first thing to think about.
我也很嘲笑这里,但我不明白你的意思。你想测试什么? Product类,ProductRepository或它们之间的交互?这是首先要考虑的问题。
#3
I'm looking at your code and it doesn't really look like you know what you're trying to achieve. Before writing any test, always ask the question: "What am I trying to prove, here?" The product you've created will be returned, but its ID will be the default value (0); this is expected behaviour, i.e. the mocking framework is working fine.
我正在查看你的代码,它看起来并不像你知道你想要实现的目标。在写任何测试之前,总是问一个问题:“我想在这里证明什么?”您将创建您创建的产品,但其ID将是默认值(0);这是预期的行为,即模拟框架工作正常。
Test doubles (mocks, stubs, fakes, spies etc.) are used in tests to provide collaborators for the class under test. They feed interesting values into or receive calls from the class under test -- they are not the focus of the test.
在测试中使用测试双打(模拟,存根,假货,间谍等)为被测试的班级提供合作者。他们将有趣的值输入或接收来自被测试类的调用 - 它们不是测试的焦点。
Your test is currently asserting on a value returned by the test double itself. From what I can understand, either you're asking for help with an out of context code snippet (the snippet itself is just an example and not the test itself), or it is real test code that is pointless.
您的测试当前正在对测试double本身返回的值进行断言。根据我的理解,你要求提供一个不完整的代码片段的帮助(片段本身只是一个例子,而不是测试本身),或者它是真正的测试代码毫无意义。
I've seen many a person tie themselves in knots with mocking frameworks. That's why, to begin with, I would recommend hand-writing your own test doubles. This helps you understand the interactions between the objects and form a clearer picture of what it is you wish to test. Again, this goes back to the question of "what am I trying to prove, here?".
我见过很多人用嘲弄的框架打结自己。这就是为什么,首先,我建议手写自己的测试双打。这有助于您了解对象之间的交互,并形成您希望测试的更清晰的图像。再次,这可以追溯到“我想在这里证明什么?”的问题。
Once you understand how to use hand-rolled test doubles to achieve a goal, you can graduate to mocking frameworks.
一旦你理解了如何使用手动测试双打来实现目标,你就可以毕业于模拟框架。
#4
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.
在设置expect时,您将Product instance p用作'期望值' - 返回(p),然后使用相同的引用来存储实际调用的返回值。
As for the Assert failure, does new Product() initialize its ProductId to 1. Seems like its being set to the default value of 0 - Hence the error.
至于Assert失败,新的Product()是否将其ProductId初始化为1.看起来它被设置为默认值0 - 因此错误。
I think the Mock framework is working.
我认为Mock框架正在运行。
#5
I would second the view of Gishu re: the Product initialization. Unless the default behaviour of IProductRepository is to return a product referenced by the ProductId of '1', your test will fail.
我会先看看Gishu的观点:产品初始化。除非IProductRepository的默认行为是返回ProductId引用的产品'1',否则您的测试将失败。
And, may I add, this failure seems to be sensible behaviour. I think that you would like you ProductRepository to be empty upon initialization.
而且,我可以补充说,这种失败似乎是明智的行为。我认为你希望ProductRepository在初始化时为空。
#6
Not exactly missing the point, Mocking is not trivial.
并不完全忽略了这一点,Mocking并非无足轻重。
In your case, you are having an IProductRepository
that, I presume, is expected to hold Products
. I assume that Products
are not added to the ProductRepositort
by default (per my earlier post), and I also assume that in order to reference a Product
, it must have a productId
(which by the way you really should mutate via a mutator).
在您的情况下,您有一个IProductRepository,我认为,它应该保留Products。我假设默认情况下没有将产品添加到ProductRepositort(根据我之前的帖子),我还假设为了引用Product,它必须有一个productId(顺便说一句,你应该通过mutator进行变异)。
If you would like to retrieve a Product
from your ProductRepository
, I think that you should add a Product
to it through the mock framework (the moq site gives an example of registering and validating right at the top of the page), and ensure that the ProductRepostory
either gives the Product
s added to it a default identifier (not recommended) or add an identifier to Product
before adding it to the ProductRepository
.
如果您想从ProductRepository中检索产品,我认为您应该通过模拟框架向其添加产品(moq网站提供了在页面顶部注册和验证的示例),并确保ProductRepostory为产品添加了默认标识符(不推荐),或者在将产品添加到ProductRepository之前向Product添加标识符。
#1
I think you are missing the point of how to use mock objects in tests...
我想你错过了如何在测试中使用模拟对象的观点......
What you are doing is mocking a ProductRepository object while simultaneously testing it. That doesn't make much sense; you should not mock the object you're testing.
您正在做的是在同时测试它的同时模拟ProductRepository对象。这没有多大意义;你不应该嘲笑你正在测试的对象。
Let's say you have a class you want to test, ProductService, and it depends on another class, IProductRepository. When you test the ProductService, you will want to mock the dependency, IProductRepository. This allows you completely control the interaction between the class under test and its (mocked) dependency.
假设您有一个要测试的类,ProductService,它依赖于另一个类IProductRepository。在测试ProductService时,您将需要模拟依赖项IProductRepository。这允许您完全控制被测试类与其(模拟)依赖项之间的交互。
As you do so, your assertions will be based on what you expect the class under test, ProductService, to do. For instance, if you call the ProductService using something like productService.GetProductById(1)
, you will expect the ProductService object to call its IProductRepository method with the correct parameter exactly once: repository.GetProductById(1)
. You may also expect the ProductService to return the same object that the IProductRepository gave it. Regardless of what the repository does, that's the responsibility of the ProductService.
当您这样做时,您的断言将基于您对待测试的类ProductService的期望。例如,如果使用productService.GetProductById(1)之类的东西调用ProductService,您将期望ProductService对象使用正确的参数调用其IProductRepository方法一次:repository.GetProductById(1)。您可能还希望ProductService返回IProductRepository为其提供的同一对象。无论存储库做什么,这都是ProductService的责任。
Having said that, your test may look something more like this:
话虽如此,您的测试可能看起来更像这样:
//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);
My syntax may be off, but hopefully the sample gets across a few points:
我的语法可能已关闭,但希望样本能够获得以下几点:
- Don't mock the system under test
- Mock the dependencies
- Base your assertions on the responsibilities of the system under test, not the dependencies
不要嘲笑被测系统
模拟依赖项
将断言基于被测系统的职责,而不是依赖关系
#2
You are creating an object, setting that object as the return value for a method, and then checking if the mock is altering the object, something that the mock is not intended to do You are basically doing this:
您正在创建一个对象,将该对象设置为方法的返回值,然后检查模拟是否正在改变该对象,这是模拟不打算做的事情您基本上是这样做的:
Product getProductById(Product p) { return p; }
...
Product p = new Product();
Assert.AreEqual(1, getProductById(p).ProductID );
when creating a new Product:
在创建新产品时:
Product p = new Product();
i guess that the default ProductID is 0, so the sentence:
我猜默认的ProductID是0,所以句子:
getProductById(p).ProductID
will obviously return 0.
显然会返回0。
I'm new to mock here too, but I don't see your point. What are you trying to test? The Product class, the ProductRepository, or interactions between them? That is the first thing to think about.
我也很嘲笑这里,但我不明白你的意思。你想测试什么? Product类,ProductRepository或它们之间的交互?这是首先要考虑的问题。
#3
I'm looking at your code and it doesn't really look like you know what you're trying to achieve. Before writing any test, always ask the question: "What am I trying to prove, here?" The product you've created will be returned, but its ID will be the default value (0); this is expected behaviour, i.e. the mocking framework is working fine.
我正在查看你的代码,它看起来并不像你知道你想要实现的目标。在写任何测试之前,总是问一个问题:“我想在这里证明什么?”您将创建您创建的产品,但其ID将是默认值(0);这是预期的行为,即模拟框架工作正常。
Test doubles (mocks, stubs, fakes, spies etc.) are used in tests to provide collaborators for the class under test. They feed interesting values into or receive calls from the class under test -- they are not the focus of the test.
在测试中使用测试双打(模拟,存根,假货,间谍等)为被测试的班级提供合作者。他们将有趣的值输入或接收来自被测试类的调用 - 它们不是测试的焦点。
Your test is currently asserting on a value returned by the test double itself. From what I can understand, either you're asking for help with an out of context code snippet (the snippet itself is just an example and not the test itself), or it is real test code that is pointless.
您的测试当前正在对测试double本身返回的值进行断言。根据我的理解,你要求提供一个不完整的代码片段的帮助(片段本身只是一个例子,而不是测试本身),或者它是真正的测试代码毫无意义。
I've seen many a person tie themselves in knots with mocking frameworks. That's why, to begin with, I would recommend hand-writing your own test doubles. This helps you understand the interactions between the objects and form a clearer picture of what it is you wish to test. Again, this goes back to the question of "what am I trying to prove, here?".
我见过很多人用嘲弄的框架打结自己。这就是为什么,首先,我建议手写自己的测试双打。这有助于您了解对象之间的交互,并形成您希望测试的更清晰的图像。再次,这可以追溯到“我想在这里证明什么?”的问题。
Once you understand how to use hand-rolled test doubles to achieve a goal, you can graduate to mocking frameworks.
一旦你理解了如何使用手动测试双打来实现目标,你就可以毕业于模拟框架。
#4
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.
在设置expect时,您将Product instance p用作'期望值' - 返回(p),然后使用相同的引用来存储实际调用的返回值。
As for the Assert failure, does new Product() initialize its ProductId to 1. Seems like its being set to the default value of 0 - Hence the error.
至于Assert失败,新的Product()是否将其ProductId初始化为1.看起来它被设置为默认值0 - 因此错误。
I think the Mock framework is working.
我认为Mock框架正在运行。
#5
I would second the view of Gishu re: the Product initialization. Unless the default behaviour of IProductRepository is to return a product referenced by the ProductId of '1', your test will fail.
我会先看看Gishu的观点:产品初始化。除非IProductRepository的默认行为是返回ProductId引用的产品'1',否则您的测试将失败。
And, may I add, this failure seems to be sensible behaviour. I think that you would like you ProductRepository to be empty upon initialization.
而且,我可以补充说,这种失败似乎是明智的行为。我认为你希望ProductRepository在初始化时为空。
#6
Not exactly missing the point, Mocking is not trivial.
并不完全忽略了这一点,Mocking并非无足轻重。
In your case, you are having an IProductRepository
that, I presume, is expected to hold Products
. I assume that Products
are not added to the ProductRepositort
by default (per my earlier post), and I also assume that in order to reference a Product
, it must have a productId
(which by the way you really should mutate via a mutator).
在您的情况下,您有一个IProductRepository,我认为,它应该保留Products。我假设默认情况下没有将产品添加到ProductRepositort(根据我之前的帖子),我还假设为了引用Product,它必须有一个productId(顺便说一句,你应该通过mutator进行变异)。
If you would like to retrieve a Product
from your ProductRepository
, I think that you should add a Product
to it through the mock framework (the moq site gives an example of registering and validating right at the top of the page), and ensure that the ProductRepostory
either gives the Product
s added to it a default identifier (not recommended) or add an identifier to Product
before adding it to the ProductRepository
.
如果您想从ProductRepository中检索产品,我认为您应该通过模拟框架向其添加产品(moq网站提供了在页面顶部注册和验证的示例),并确保ProductRepostory为产品添加了默认标识符(不推荐),或者在将产品添加到ProductRepository之前向Product添加标识符。