asp.net-web-api - 单元测试 WebApi2 传递 header 值

标签 asp.net-web-api moq xunit xunit.net asp.net-web-api2

我正在使用 WebApi2 开发一个项目。在我的测试项目中,我使用了 Moq 和 XUnit。

到目前为止,测试 api 非常简单,可以像 GET 一样

  [Fact()]
    public void GetCustomer()
    {
        var id = 2;

        _customerMock.Setup(c => c.FindSingle(id))
            .Returns(FakeCustomers()
            .Single(cust => cust.Id == id));

        var result = new CustomersController(_customerMock.Object).Get(id);

        var negotiatedResult = result as OkContentActionResult<Customer>;
        Assert.NotNull(negotiatedResult);
        Assert.IsType<OkNegotiatedContentResult<Customer>>(negotiatedResult);
        Assert.Equal(negotiatedResult.Content.Id,id);
    }

现在我要处理一些复杂的事情,我需要从请求 header 中访问值。

我通过扩展 IHttpActionResult 创建了我自己的 Ok() 结果

   public OkContentActionResult(T content,HttpRequestMessage request)
    {
        _request = request;
        _content = content;
    }

这让我有一个小助手可以从请求中读取 header 值。

 public virtual IHttpActionResult Post(Customer customer)
    {
        var header = RequestHeader.GetHeaderValue("customerId", this.Request);

        if (header != "1234")

我要如何使用虚拟请求设置起订量?

我花了大约一个小时的时间寻找一个允许我使用 webapi 执行此操作的示例,但我似乎找不到任何东西。

到目前为止.....我很确定它对 api 是错误的,但我有

      // arrange
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var headers = new NameValueCollection
        {
            { "customerId", "111111" }
        };
        request.Setup(x => x.Headers).Returns(headers);
        request.Setup(x => x.HttpMethod).Returns("GET");
        request.Setup(x => x.Url).Returns(new Uri("http://foo.com"));
        request.Setup(x => x.RawUrl).Returns("/foo");
        context.Setup(x => x.Request).Returns(request.Object);
        var controller = new Mock<ControllerBase>();
        _customerController = new CustomerController()
        {
            //  Request = request,

        };

我不太确定接下来需要做什么,因为我过去不需要设置模拟 HttpRequestBase。

谁能推荐一篇好文章或为我指明正确的方向?

谢谢!!!

最佳答案

我认为您应该避免读取 Controller 中的 header ,以便更好地分离关注点(您不需要从 Controller 中的请求主体读取客户,对吗?)和可测试性。

我将如何创建一个 CustomerId 类(这是可选的。请参阅下面的注释)和 CustomerIdParameterBinding

public class CustomerId
{
    public string Value { get; set; }
}

public class CustomerIdParameterBinding : HttpParameterBinding
{
    public CustomerIdParameterBinding(HttpParameterDescriptor parameter) 
    : base(parameter)
    {
    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        actionContext.ActionArguments[Descriptor.ParameterName] = new CustomerId { Value = GetIdOrNull(actionContext) };
        return Task.FromResult(0);
    }

    private string GetIdOrNull(HttpActionContext actionContext)
    {
        IEnumerable<string> idValues;
        if(actionContext.Request.Headers.TryGetValues("customerId", out idValues))
        {
            return idValues.First();
        }
        return null;
    }
}

编写 CustomerIdParameterBinding

config.ParameterBindingRules.Add(p =>
{
    return p.ParameterType == typeof(CustomerId) ? new CustomerIdParameterBinding(p) : null;
});

然后在我的 Controller 中

public void Post(CustomerId id, Customer customer)

测试参数绑定(bind)

public void TestMethod()
{
    var parameterName = "TestParam";
    var expectedCustomerIdValue = "Yehey!";

    //Arrange
    var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri");
    requestMessage.Headers.Add("customerId", expectedCustomerIdValue );

    var httpActionContext = new HttpActionContext
    {
        ControllerContext = new HttpControllerContext
        {
            Request = requestMessage
        }
    };

    var stubParameterDescriptor = new Mock<HttpParameterDescriptor>();
    stubParameterDescriptor.SetupGet(i => i.ParameterName).Returns(parameterName);

    //Act
    var customerIdParameterBinding = new CustomerIdParameterBinding(stubParameterDescriptor.Object);
    customerIdParameterBinding.ExecuteBindingAsync(null, httpActionContext, (new CancellationTokenSource()).Token).Wait();

    //Assert here
    //httpActionContext.ActionArguments[parameterName] contains the CustomerId
}

注意:如果您不想创建 CustomerId 类,您可以使用自定义 ParameterBindingAttribute 注释您的参数。像这样

public void Post([CustomerId] string customerId, Customer customer)

See here on how to create a ParameterBindingAttribute

关于asp.net-web-api - 单元测试 WebApi2 传递 header 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21152777/

相关文章:

azure - Web API 和 Azure API 应用程序有什么区别?

c# - 在 C# 单元测试中使用 HttpContext.GetTokenAsync

c# - 这还是Mock上的验证测试吗?

c# - xunit 是否有参数化测试夹具?

c# - 如何使用 xUnit 测试 ref struct 方法是否抛出异常?

c# - OData v4 函数字符串参数中的字母数字字符串被解析为 int 为字符串,或 null

c# - 使用 Entity Framework Core 更新相关数据

c# - 无法添加类型为 'add' 且唯一键属性 'name' 设置为 'Access-Control-Allow-Origin' 的重复集合条目

c# - 异步事件监听器的 Moq Raise

asp.net-core - 填充 IConfiguration 以进行单元测试