2016-07-14 85 views
2

我在學習webapi,並且偶然發現了一個問題。我在做的培訓課程展示瞭如何通過返回帶有下一個和上一個鏈接的響應標題來進行分頁。但它使用HttpContext.Current.Response.Headers.Add()發回下一個鏈接,上一個鏈接和總頁數。帶響應頭的單元測試webapi控制器

我也試圖爲控制器實現單元測試。問題似乎是在運行單元測試時HttpContext.Currentnull。我讀了一個地方,我不應該爲webapi,因爲它不可測試,但我不知道我應該用什麼。

這裏是我的代碼位指示:

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(); 
    } 

} 

我希望有人能夠幫助我。這可能是因爲我採用實現分頁的方式導致了錯誤的路徑。不過,我很可能需要爲任何方式添加響應標頭。

+0

不要模擬HttpContext。改爲編寫內存集成測試。看例子:http://stephenzeng.com/Home/View/17。 –

回答

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如果您分配通過以下測試檢查爲X-Pagination頭球時默認ActionContext

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(); 
} 

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")); 
} 
+0

如果請求擁有流,例如,我該如何測試?在多部分上下文中。我想喜歡這樣的測試,但我最終在內存中使用HttpServer,所以請求被處理爲真實世界的情況 – ntohl

+0

@ntohl只是用需要的東西來填充要運行被測系統的請求。相應地設置請求的內容屬性。 – Nkosi

+0

我已經管理了。謝謝。但是在CORS添加到平等之後,我必須檢查標題,如果它們是從'[EnableCors(「*」,「Origin」,「POST」)]'正確生成的,並且我也無法檢查我的自定義MessageHandler做了迴應。 – ntohl