具有响应头的单元测试webapi控制器

时间:2016-07-14 15:13:09

标签: c# unit-testing nunit asp.net-web-api2 moq

我正在尝试学习webapi,偶然发现了一个问题。我正在做的培训课程通过返回带有下一个和上一个链接的响应头来展示如何进行分页。但是,它使用HttpContext.Current.Response.Headers.Add()发回下一个链接,上一个链接和总页数。

我也在尝试为控制器实现单元测试。在运行单元测试时,问题似乎是HttpContext.Currentnull。我读到某个地方我不应该为{web}修改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();
    }

}

我希望有人能够帮助我。可能是因为实现分页的方式导致我走错了路。但是,我可能需要为任何方式添加响应头。

1 个答案:

答案 0 :(得分:0)

你应该避免将自己与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时,您的控制器设置会导致空引用错误,因为您在分配默认Request时将控制器的ActionContext重置为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"));
}