模拟请求对象用于Web API单元测试

本文关键字:API 单元测试 Web 用于 请求 对象 模拟 | 更新日期: 2023-09-27 17:49:35

我想单元测试我的web API控制器。我有一个问题,我的一个动作方法(POST)是需要Request对象的值,以获得控制器名称。我使用nsubstitute, FluentAssertions来支持我的单元测试

我的控制器代码是这样的:

public class ReceiptsController : BaseController
{
    public ReceiptsController(IRepository<ReceiptIndex> repository) : base(repository) { }
    ..... Other action code
    [HttpPost]
    public IHttpActionResult PostReceipt(string accountId, [FromBody] ReceiptContent data, string userId = "", string deviceId = "", string deviceName = "")
    {
        if (data.date <= 0)
        {
            return BadRequest("ErrCode: Save Receipt, no date provided");
        }
        var commonField = new CommonField()
        {
            AccountId = accountId,
            DeviceId = deviceId,
            DeviceName = deviceName,
            UserId = userId
        };
        return PostItem(repository, commonField, data);
    }
}

控制器的基类:

public abstract class BaseController : ApiController
{
    protected IRepository<IDatabaseTable> repository;
    protected BaseController(IRepository<IDatabaseTable> repository)
    {
       this.repository = repository;
    }
    protected virtual IHttpActionResult PostItem(IRepository<IDatabaseTable> repo, CommonField field, IContent data)
    {
        // How can I mock Request object on this code part ???
        string controllerName = Request.GetRouteData().Values["controller"].ToString();
        var result = repository.CreateItem(field, data);
        if (result.Error)
        {
            return InternalServerError();
        }
        string createdResource = string.Format("{0}api/accounts/{1}/{2}/{3}", GlobalConfiguration.Configuration.VirtualPathRoot, field.AccountId,controllerName, result.Data);
        var createdData = repository.GetItem(field.AccountId, result.Data);
        if (createdData.Error)
        {
            return InternalServerError();
        }
        return Created(createdResource, createdData.Data);
    }
}

这是我成功创建场景的单元测试:

[Test]
public void PostClient_CreateClient_ReturnNewClient()
{
    // Arrange
    var contentData = TestData.Client.ClientContentData("TestBillingName_1");
    var newClientId = 456;
    var expectedData = TestData.Client.ClientData(newClientId);
    clientsRepository.CreateItem(Arg.Any<CommonField>(), contentData)
         .Returns(new Result<long>(newClientId)
         {
            Message = ""
         });
     clientsRepository.GetItem(accountId, newClientId)
         .Returns(new Result<ContactIndex>(expectedData));
     // Act
     var result = _baseController.PostClient(accountId, contentData, userId);
     // Asserts
      result.Should().BeOfType<CreatedNegotiatedContentResult<ContactIndex>>()
                .Which.Content.ShouldBeEquivalentTo(expectedData);
 }

我不知道是否有任何方法从控制器中提取请求对象,或者是否有任何方法在单元测试中模拟它?现在这个代码Request.GetRouteData()在单元测试中返回null

模拟请求对象用于Web API单元测试

你可以做一个获取请求数据的接口(传递请求对象给它)。实现该接口并在控制器中作为依赖项使用。然后,您可以在单元测试中轻松模拟此接口实现。

我终于找到解决这个问题的方法了。所以基本上我必须创建一些配置相关的东西,使我的单元测试工作。

我为这个

创建一个helpers
public static class Helpers
    {
        public static void SetupControllerForTests(ApiController controller)
        {
            var config = new HttpConfiguration();
            var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/products");
            var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
            var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
            controller.ControllerContext = new HttpControllerContext(config, routeData, request);
            controller.Request = request;
            controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
        }
    }

然后在我的测试setup上通过我的测试控制器

[SetUp]
public void SetUp()
{
    clientsRepository = Substitute.For<IRepository<ContactIndex>>();
    _baseController = new ClientsController(clientsRepository);
    Helpers.SetupControllerForTests(_baseController);
}

我不知道这是否是最好的方法,但我更喜欢这种方式,而不是创建一个新的接口,并注入到我的控制器