设置一个仅公开属性的模拟(Moq)class

Setup a Mocked (Moq) class that only exposes properties

我正在尝试模拟(使用最小起订量)一个 class 在 class 上设置一个 return 对象,它只公开两个属性。

在我有限的起订量经验中,我通常会使用 Setup() lamda 来定义方法调用,然后 Returns() 吐回所需的输出。

我在这里失败的是 Setup()。没有 "method" 可以调用,因为构造函数会完成工作,填充两个属性,然后 returns.

我想嘲笑的 class...显然是假的:

public class CarResponse
{
    public IMetaModel meta { get; set; }
    public List<ICarModel> cars { get; set; }

    public CarResponse(Common.Models.Car car)
    {
        this.cars = new List<ICarModel>();
    }
}

我微弱的嘲讽尝试:

private Mock<CarResponse> _carResponse = new Mock<CarResponse>(MockBehavior.Strict);
_carResponse.Setup( ????? ).Returns(new CarResponse() { meta = new MetaModelV2(), cars = foo });

为了进一步说明...这是我尝试为其编写单元测试的代码:

public HttpResponseMessage AddPickup()
{
     //....code removed for brevity....

    //this repository is mocked and returns the object exactly as I want it
     var car = carRepository.GetCar(carId);

   if (!errorInfo.Any()) //This check is bogus it never gets sets
   {
     RequestHelper rqh = new RequestHelper();

     response = rqh.CreateResponse(Request, HttpStatusCode.OK, new CarResponse(car));
}

我的单元测试:

[TestMethod]
public void AddValidPickupCorrectResponse()
{
   //arrange
   //...lots of code here left off for setting up http context etc

  //act---
  var response = controller.AddPickup();

  //assert

}

如果我按照建议使用固定对象,我将如何 "hook" 将其用于测试代码。例如,我编写了一个单元测试,它使用我的预装对象而不是最小起订量,但是如何让 SUT 使用该预装对象?

您可以使用 SetupGet 和 SetupSet 模拟属性。但是,我不认为你可以模拟具体 类.

如果您正在处理值类型,您可能会发现更容易不去打扰模拟,而只使用预制对象。

很少有问题会妨碍对上述代码进行正确的单元测试:

  1. 新建响应助手
  2. 更新 CarResponseObject

本质上,除非真正的 POCO 中的 class(即只有具有 public setter 和 getter 的数据),否则使用 "new" 是单元测试的杀手。 IE。它不是单元测试(单独测试 unit/method)。它测试 CarResponse ctor 的行为,以及 RequestHelper 的工作。

考虑以下更改:

  1. 注入 RequestHelper(这样你就可以模拟 CreateResponse 方法)
  2. 使用并注入一些映射工厂,它可以从 Car 创建 CarResponseObjects。
  3. 考虑 CarResponse 来实现类似 IResponse 的功能,这样您的 RequestHelper 或工厂就可以 return 接口。

有了以上所有内容,您的测试将如下所示(伪代码,不完整):

//arrange
//....
var carInDB = new Car();
_repoMock.Setup(...).Returns(car);

var carResponse = Mock.Of<IResponse>();
_mapperMock.Setup(m=>m.CreateResponse(car).Returns(carResponse);

var responseFromHelper = new WhateverResponseIsNeeded(); //(or create a new mock IResponse - note! new mock, different than car response
_helperMock.Setup(_controller.Request, HttpStatusCode.OK, carResponse).Returns(responseFromHelper);

//act
var response = _controller.AddPickup();

//assert
response.Should().Be.SameInstanceAs(responseFromHelper)