Asp.net web api 单元测试WebApi2传递头值

Asp.net web api 单元测试WebApi2传递头值,asp.net-web-api,moq,xunit,xunit.net,asp.net-web-api2,Asp.net Web Api,Moq,Xunit,Xunit.net,Asp.net Web Api2,我正在使用WebApi2进行一个项目。在我的测试项目中,我使用Moq和XUnit 到目前为止,测试一个api是非常直接的 [Fact()] public void GetCustomer() { var id = 2; _customerMock.Setup(c => c.FindSingle(id)) .Returns(FakeCustomers() .Single(cust =>

我正在使用WebApi2进行一个项目。在我的测试项目中,我使用Moq和XUnit

到目前为止,测试一个api是非常直接的

  [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);
    }
这允许我有一个从请求中读取头值的小助手

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

        if (header != "1234")
我如何用虚拟请求设置Moq

我已经花了一个小时左右的时间寻找一个例子,它允许我用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,

        };
//排列
var context=newmock();
var request=newmock();
var headers=newnamevalueCollection
{
{“customerId”,“111111”}
};
request.Setup(x=>x.Headers).返回(Headers);
Setup(x=>x.HttpMethod).Returns(“GET”);
Setup(x=>x.Url).Returns(新Uri(“http://foo.com"));
Setup(x=>x.RawUrl).Returns(“/foo”);
Setup(x=>x.Request).Returns(Request.Object);
var controller=newmock();
_customerController=新customerController()
{
//请求=请求,
};
我不确定下一步我需要做什么,因为我过去不需要建立一个模拟的HttpRequestBase

有人能给我推荐一篇好文章或给我指出正确的方向吗


谢谢你

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

我要做的是创建一个
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;
    }
}
然后在我的控制器里

public void Post(CustomerId id, Customer customer)
测试参数绑定

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
}

感谢@LostInComputer对您的详细回复。我同意api控制器在验证头部时感觉不好。然而,我们的api的一部分将要求用户在标题中提供customerid。我曾考虑使用actionfilter来验证这一点,但我仍然渴望能够测试这一点。customerid是我们需要测试的标题中保存的大约4个值中的第一个。我建议的解决方案是您可以将测试分为两个。1:测试是否向控制器2提供了有效的customerId。测试customerId是否通过参数绑定从标头中检索。希望我们能从某人那里得到另一个答案。我也对其他想法很好奇。
public void Post(CustomerId id, Customer customer)
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
}
public void Post([CustomerId] string customerId, Customer customer)