我正在尝试学习webapi,偶然发现了一个问题。我正在做的培训课程展示了如何通过返回带有下一个和上一个链接的响应标头来进行分页。但是,它使用HttpContext.Current.Response.Headers.Add()
发回下一个链接、上一个链接和总页数。
我还在尝试为控制器实现单元测试。问题似乎是在运行单元测试时HttpContext.Current
null
。我在某处读到我不应该对 webapi HttpContext.Current
,因为它不可测试,但我不确定我应该使用什么。
这是我的收费代码:
public partial class CandidateManagerController
{
private readonly ICandidateManager _candidateManagerV2;
public CandidateManagerController(ICandidateManager candidateManager)
{
_candidateManagerV2 = candidateManager;
}
[VersionedRoute("CandidateManager", 2, Name="CandidateManagerV2")]
public IHttpActionResult Get(int page = 1, int pageSize = 1)
{
try
{
var totalCount = 0;
var totalPages = 0;
var result = _candidateManagerV2.GetCandidates(out totalCount, out totalPages, page, pageSize);
var urlHelper = new UrlHelper(Request);
var prevLink = page > 1
? urlHelper.Link("CandidateManagerV2",
new
{
page = page - 1,
pageSize = pageSize,
})
: "";
var nextLink = page < totalPages ? urlHelper.Link("CandidateManagerV2",
new
{
page = page + 1,
pageSize = pageSize
}) : "";
var paginationHeader = new
{
currentPage = page,
pageSize = pageSize,
totalCount = totalCount,
totalPages = totalPages,
previousPageLink = prevLink,
nextPageLink = nextLink
};
HttpContext.Current.Response.Headers.Add("X-Pagination", Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));
return Ok(result);
}
catch (Exception exp)
{
return InternalServerError();
}
}
}
这是我的单元测试。请注意,我使用的是Nunit和Moq:
[TestFixture]
public class CandidateManagerControllerV2Tests
{
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_List_Of_Candidate_Objects()
{
var testList = new List<Candidate>();
testList.Add(new Candidate() { CandidateId = 1, Name = "Mr", Surname = "Flibble" });
testList.Add(new Candidate() { CandidateId = 2, Name = "Arnold", Surname = "Rimmer" });
var totalCount = 0;
var totalPages = 0;
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.GetCandidates(out totalCount, out totalPages, It.IsAny<int>(), It.IsAny<int>())).Returns(testList);
var controller = new CandidateManagerController(mockManager.Object);
SetupControllerForTests(controller);
var result = controller.Get(1, 1);
}
private static void SetupControllerForTests(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/candidatemanager");
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;
controller.ActionContext=new HttpActionContext();
}
}
我希望有人能够帮助我。可能是我在实现分页的方式上走上了错误的道路。但是,我可能需要以任何方式为某些内容添加响应标头。
要测试响应标头,您需要执行以下操作:
- 在
TestInitialize
中使用控制器上下文初始化控制器 - 调用在其中添加自定义标头的控制器
- 在
TestMethod
断言
仅当您在控制器类中添加标头时,它才有效:
HttpContext.Response.Headers.Add("x-custom-header", "value");
例:
public class MyControllerTests
{
private MyController _controller;
[TestInitialize]
public void Setup()
{
_controller= new MyController();
_controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext(),
};
}
[TestMethod]
public async Task GetAsyncShouldContainCutomHeader()
{
// Act
await _controller.GetAsync().ConfigureAwait(false);
// Assert
Assert.IsTrue(_controller.Response.Headers.ContainsKey("x-custom-header"));
Assert.IsTrue(_controller.Response.Headers["x-custom-header"].Equals("value"));
}
}
您应该避免将自己与HttpContext
耦合。
这是另一种方法,用于设置标头并仍然能够按预期对其进行单元测试。您可以创建一个HttpResponseMessage
,根据需要添加标头,然后从中创建ResponseMessageResult
:
//...code removed for brevity
var response = Request.CreateResponse(HttpStatusCode.OK, result);
response.Headers.Add("X-Pagination", Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));
IHttpActionResult ok = ResponseMessage(response);
return ok;
还应注意,在创建 UrlHelper 时,控制器设置将导致空引用错误,因为在分配默认ActionContext
时,会将控制器的Request
重置为 null
private static void SetupControllerForTests(ApiController controller) {
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/candidatemanager");
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;
//commented this out as it was causing Request to be null
//controller.ActionContext=new HttpActionContext();
}
检查X-Pagination
标头时通过以下测试
public async Task Should_Return_Paged_List_Of_Candidate_Objects() {
//Arrange
var testList = new List<Candidate>();
testList.Add(new Candidate() { CandidateId = 1, Name = "Mr", Surname = "Flibble" });
testList.Add(new Candidate() { CandidateId = 2, Name = "Arnold", Surname = "Rimmer" });
var totalCount = 0;
var totalPages = 0;
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.GetCandidates(out totalCount, out totalPages, It.IsAny<int>(), It.IsAny<int>())).Returns(testList);
var controller = new CandidateManagerController(mockManager.Object);
SetupControllerForTests(controller);
//Act
var response = await controller.Get(1, 1).ExecuteAsync(System.Threading.CancellationToken.None);
//Assert
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(HttpResponseMessage));
Assert.IsTrue(response.Headers.Contains("X-Pagination"));
}