首页 > 解决方案 > Moq 设置返回对对象的引用

问题描述

假设我有一个名为 MyRequestHandler 的简单类,它有一个名为 ProcessRequest 的方法,该方法只接受一个请求对象,将其映射到返回对象并返回该对象。(这显然是我正在研究的更复杂的方法/测试的一个非常简单的示例)。

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;
  }
}

现在对于单元测试(使用 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);

这里的问题是 Moq 返回(我想应该很明显它是)对 returnObject 的引用,所以我的断言将始终通过,即使我的方法要在返回对象之前更改值。现在我可以在 Moq Setup/Return 中实例化一个新的 MyReturnObject 并通过我给新的值比较 MyInt 和 MyString,但是如果它是一个非常复杂的对象,有 20 个属性和对象列表怎么办?也许我想使用 AutoFixture 创建要返回的对象并使用 DeepEqual 来比较它们?这甚至可能吗?我是不是看错了,还是我必须在设置/返回中进行某种类型的克隆才能使其正常工作?

标签: c#unit-testingmoq

解决方案


我不相信有内置功能来检测被测方法没有改变传递给它的对象。

选项:

  • 确保返回对象是不可变的——要么让它们一开始就不可变,要么返回没有“set”方法的接口,并使用通过模拟创建的实例
  • 为“预期”和“模拟”值创建单独的实例,然后逐个比较属性。有很多帮助库可以做到这一点(我喜欢 FluentAssertions)。
  • 只需断言单个属性而不是比较对象 - 适用于少量字段。

如果可能的话,我更喜欢不可变对象 - 防止编写错误代码的可能性,从而减少所需的测试量。


推荐阅读