最小起订量设置 returns 对对象的引用

Moq setup returns reference to object

假设我有一个名为 MyRequestHandler 的简单 class,它有一个名为 ProcessRequest 的方法,它只需要一个请求对象,将其映射到一个 return 对象,然后 returns那个对象。 (这显然是我正在处理的更复杂 method/test 的一个非常简单的示例)。

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 returns(我想它应该是显而易见的)对 returnObject 的引用,所以我的断言将始终通过,即使我的方法在 returning 对象之前更改值。现在我可以在 Moq Setup/Return 中实例化一个新的 MyReturnObject 并根据我赋予新对象的值比较 MyInt 和 MyString,但是如果它是一个具有 20 个属性和对象列表的非常复杂的对象怎么办?也许我想使用 AutoFixture 来创建被 returned 的对象并使用 DeepEqual 来比较它们?这可能吗?我是不是看错了,还是我必须在 Setup/Return 中进行某种类型的克隆才能使其工作?

在这种情况下,您没有收到新数据并且可以验证行为

在这种情况下,内部状态没有价值

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

一些细节

  1. 我们无法与他人共享写入权限,所以我假设您有

    public class MyRequestObject { int RequestInt { 得到;私有集; } 字符串 RequestString { 得到;私有集; } }

否则你总是应该测试参数突变。你可以想象有 10 个参与者被深度调用,他们每个人都应该进行这样的测试。这些测试将无法抵抗变化,它们对新属性没有任何作用。

  1. 最好有良好的编码习惯,偶尔做codereview。例如,有人可以从 属性 中随机删除 private 并且无法通过任何测试捕获它。

  2. 例子中有很多好的做法"write test before of code"等等

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

选项:

  • 确保 return 对象是不可变的——要么让它们一开始就不可变,要么通过 returning 没有 "set" 方法的接口和通过 mocks[=20 创建的实例=]
  • 为 "expected" 和 "mocked" 值创建单独的实例,然后比较 属性-by-属性。有很多辅助库可以做到这一点(我喜欢 FluentAssertions)。
  • 只需断言个别属性而不是比较对象 - 适用于少量字段。

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