Moq设置返回对象的引用

问题描述 投票:0回答:1

比方说,我有一个简单的类,叫做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 SetupReturn中实例化一个新的MyReturnObject,然后通过我给新对象的值来比较MyInt和MyString,但是如果它是一个非常复杂的对象,有20个属性和对象列表呢? 也许我想使用AutoFixture来创建被返回的对象,然后使用DeepEqual来比较它们? 这可能吗? 是我看错了,还是我必须在SetupReturn中进行某种类型的克隆才能使之工作?

c# unit-testing moq
1个回答
1
投票

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

选项:确保返回对象是不可变的。

  • 确保返回对象是不可变的 -- 要么从一开始就是不可变的,要么通过mocks创建的实例来返回没有 "设置 "方法的接口。
  • 为 "预期的 "和 "模拟的 "值创建单独的实例,然后逐个属性进行比较。有很多辅助库可以做到这一点(我喜欢FluentAssertions)。
  • 只需对单个属性进行断言,而不是比较对象--对于少量的字段来说,这样做很好。

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


0
投票

在这种情况下,你没有收到一个新的数据,可以验证行为。

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

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 { get; private set; } string RequestString { get; private set; }}。

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

  1. 最好是有一个好的编码约定,有时做codereview。例如,有人可以随机删除 私人 的属性,并且它不能被任何测试所捕获。

  2. 有很多好的做法,比如 "在代码之前写测试 "等等。

© www.soinside.com 2019 - 2024. All rights reserved.