单元测试WebApi2传递标题值

时间:2014-01-16 03:23:11

标签: 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);
    }

现在我正在进行一些有点复杂的事情,我需要从请求标题中访问值。

我通过扩展IHttpActionResult

创建了我自己的Ok()结果
   public OkContentActionResult(T content,HttpRequestMessage request)
    {
        _request = request;
        _content = content;
    }

这允许我有一个小帮助程序从请求中读取标头值。

 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,

        };

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

任何人都可以推荐一篇好文章或指出我正确的方向吗?

谢谢!!!

1 个答案:

答案 0 :(得分:7)

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

我将如何创建一个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;
});

然后在我的控制器中

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
}

注意:如果您不想创建CustomerId课程,可以使用自定义ParameterBindingAttribute为参数添加注释。像这样

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

See here on how to create a ParameterBindingAttribute