我正在使用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。
任何人都可以推荐一篇好文章或指出我正确的方向吗?
谢谢!!!
答案 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)