简体   繁体   English

起订量设置返回对 object 的引用

[英]Moq setup returns reference to object

Let's say I have a simple class called MyRequestHandler, and it has a method called ProcessRequest that simply takes a request object, maps it to a return object and returns that object. Let's say I have a simple class called MyRequestHandler, and it has a method called ProcessRequest that simply takes a request object, maps it to a return object and returns that object. (This is obviously a very simple example of a much more complex method/test that I'm working on). (这显然是我正在研究的更复杂的方法/测试的一个非常简单的示例)。

public class MyRequestHandler
{
  private IMapper _mapper;

  public MyRequestHandler(IMapper maper)
  {
    _mapper = mapper;
  }

  public MyReturnObject ProcessRequest(MyRequestObject requestObject)
  {
    MyReturnObject returnObject = _mapper.Map<MyReturnObject>(requestObject);
    return returnObject;
  }
}

Now for unit testing (using Xunit), I want to test the ProcessRequest method, but obviously want to Moq the Map method, as such:现在进行单元测试(使用 Xunit),我想测试 ProcessRequest 方法,但显然想要 Moq Map 方法,如下所示:

MyRequestObject requestObject = new RequestObject()
{
  RequestInt = 1,
  RequestString = "Hello"
};

MyReturnObject returnObject = new MyReturnObject()
{
  MyInt = 1,
  MyString = "Hello"
};

Mock<IMapper> mockMapper = new Mock<IMapper>();
mockMapper.Setup(m => m.Map<MyRequestObject>(requestObject)).Returns(returnObject);

MyRequestHandler requestHandler = new MyRequestHandler(mockMapper.Object);
MyReturnObject response = requestHandler.ProcessRequest(requestObject);

Assert.Equal(returnObject.MyInt, response.MyInt);
Assert.Equal(returnObject.MyString, response.MyString);

The problem here is that Moq returns (and I guess it should be obvious that it is) a reference to returnObject, so my Asserts will always pass, even if my method were to change a value prior to returning the object.这里的问题是 Moq 返回(我想应该很明显)一个对 returnObject 的引用,所以我的断言将始终通过,即使我的方法要在返回 object 之前更改一个值。 Now I could instantiate a new MyReturnObject in the Moq Setup/Return and compare the MyInt and MyString by the values I give to the new one, but what if it's a really complex object with 20 properties and lists of objects?现在我可以在 Moq Setup/Return 中实例化一个新的 MyReturnObject,并根据我给新的值比较 MyInt 和 MyString,但是如果它是一个非常复杂的 object,有 20 个属性和对象列表怎么办? Maybe I want to use AutoFixture to create the object being returned and use DeepEqual to compare them?也许我想使用 AutoFixture 创建返回的 object 并使用 DeepEqual 来比较它们? Is this even possible?这甚至可能吗? Am I looking at this wrong, or do I have to do some type of cloning in the Setup/Return to make this work?我是不是看错了,还是我必须在设置/返回中进行某种类型的克隆才能使其正常工作?

I don't believe there is built in functionality to detect that method under test did not change object passed to it.我不相信有内置功能来检测被测方法没有改变 object 传递给它。

Options:选项:

  • make sure that return objects are immutable - either by having them immutable to start with or by returning interface without "set" methods with an instance created via mocks确保返回对象是不可变的——要么让它们一开始就不可变,要么返回没有“set”方法的接口,并使用通过模拟创建的实例
  • create separate instance for "expected" and "mocked" values and then compare property-by-property.为“预期”和“模拟”值创建单独的实例,然后逐个比较属性。 There are plenty of helper libraries to do so (I like FluentAssertions).有很多帮助库可以做到这一点(我喜欢 FluentAssertions)。
  • just assert on individual properties instead of comparing objects - works fine for small number of fields.只需断言单个属性而不是比较对象 - 适用于少量字段。

If possible I'd prefer immutable objects - that prevent possibility of writing wrong code and thus decreases amount of testing needed.如果可能的话,我更喜欢不可变对象 - 防止编写错误代码的可能性,从而减少所需的测试量。

In this case you didn't receive a new data and can verify behavior在这种情况下,您没有收到新数据并且可以验证行为

Internal state is not valuable in this case在这种情况下,内部 state 没有价值

var requestObject = new RequestObject();
var returnObject = new MyReturnObject();

...
var actual = requestHandler.ProcessRequest(requestObject);

Assert.AreSame(returnObject, actual);
mockMapper.Verify(
   instance => instance.Map<MyRequestObject>(requestObject),
   Times.Once);

Some details一些细节

  1. we can't share write access with others, so i assume you have我们不能与其他人共享写权限,所以我假设你有

    public class MyRequestObject { int RequestInt { get;公共 class MyRequestObject { int RequestInt { get; private set;私人套装; } string RequestString { get; } 字符串请求字符串 { 获取; private set;私人套装; } } } }

otherwise you always should test for parameter mutation.否则你总是应该测试参数突变。 You can imagine 10 participants called in depth and each of them should have such tests.你可以想象 10 名参与者被深度调用,每个人都应该进行这样的测试。 These tests will weak against changes, they do nothing with new properties.这些测试对变化很弱,它们对新属性没有任何作用。

  1. It is better to have good coding convention and do codereview sometimes.最好有良好的编码约定并有时进行代码审查。 In example someone can randomly remove private from property and it can't be catched with any tests.例如,某人可以从财产中随机删除私有,并且无法通过任何测试来捕获它。

  2. There are many good practices in example "write test before of code" and so on例如“在代码之前编写测试”等有很多好的实践

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM