2015-05-18 96 views
2

我想單元測試我的Web API控制器。我的一個操作方法(POST)有一個問題,它需要從Request對象中獲取值,以獲取控制器名稱。 我使用NSubtitute,FluentAssertions支持我的單元測試Web API單元測試的模擬請求對象

這是我的控制器代碼如下所示:

public class ReceiptsController : BaseController 
{ 
    public ReceiptsController(IRepository<ReceiptIndex> repository) : base(repository) { } 
    ..... Other action code 

    [HttpPost] 
    public IHttpActionResult PostReceipt(string accountId, [FromBody] ReceiptContent data, string userId = "", string deviceId = "", string deviceName = "") 
    { 
     if (data.date <= 0) 
     { 
      return BadRequest("ErrCode: Save Receipt, no date provided"); 
     } 

     var commonField = new CommonField() 
     { 
      AccountId = accountId, 
      DeviceId = deviceId, 
      DeviceName = deviceName, 
      UserId = userId 
     }; 
     return PostItem(repository, commonField, data); 
    } 
} 

和基類爲我的控制器:

public abstract class BaseController : ApiController 
{ 
    protected IRepository<IDatabaseTable> repository; 

    protected BaseController(IRepository<IDatabaseTable> repository) 
    { 
     this.repository = repository; 
    } 

    protected virtual IHttpActionResult PostItem(IRepository<IDatabaseTable> repo, CommonField field, IContent data) 
    { 
     // How can I mock Request object on this code part ??? 
     string controllerName = Request.GetRouteData().Values["controller"].ToString(); 

     var result = repository.CreateItem(field, data); 

     if (result.Error) 
     { 
      return InternalServerError(); 
     } 

     string createdResource = string.Format("{0}api/accounts/{1}/{2}/{3}", GlobalConfiguration.Configuration.VirtualPathRoot, field.AccountId,controllerName, result.Data); 
     var createdData = repository.GetItem(field.AccountId, result.Data); 

     if (createdData.Error) 
     { 
      return InternalServerError(); 
     } 
     return Created(createdResource, createdData.Data); 
    } 
} 

這是我的單元測試成功創建場景:

[Test] 
public void PostClient_CreateClient_ReturnNewClient() 
{ 
    // Arrange 
    var contentData = TestData.Client.ClientContentData("TestBillingName_1"); 
    var newClientId = 456; 
    var expectedData = TestData.Client.ClientData(newClientId); 

    clientsRepository.CreateItem(Arg.Any<CommonField>(), contentData) 
     .Returns(new Result<long>(newClientId) 
     { 
      Message = "" 
     }); 

    clientsRepository.GetItem(accountId, newClientId) 
     .Returns(new Result<ContactIndex>(expectedData)); 

    // Act 
    var result = _baseController.PostClient(accountId, contentData, userId); 

    // Asserts 
     result.Should().BeOfType<CreatedNegotiatedContentResult<ContactIndex>>() 
       .Which.Content.ShouldBeEquivalentTo(expectedData); 
} 

我不喜歡不知道是否有任何方法從控制器中提取Request對象,或者有什麼方法在單元測試中嘲笑它? 眼下這段代碼Request.GetRouteData()在單元測試返回null

回答

5

你可以讓一個接口來獲取請求數據(通過Request對象的話)。實現該接口並在Controller中作爲依賴項使用。然後你可以在你的單元測試中輕鬆地模擬這個接口實現。

1

我終於找到一個方法來解決這個問題。所以基本上我必須創建一些配置相關的東西來使我的單元測試工作。

我創建一個helpers類此

public static class Helpers 
    { 
     public static void SetupControllerForTests(ApiController controller) 
     { 
      var config = new HttpConfiguration(); 
      var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/products"); 
      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; 
     } 
    } 

然後通過我的測試控制器在我的測試setup

[SetUp] 
public void SetUp() 
{ 
    clientsRepository = Substitute.For<IRepository<ContactIndex>>(); 
    _baseController = new ClientsController(clientsRepository); 

    Helpers.SetupControllerForTests(_baseController); 
} 

我不知道這是做一個最好的方法,但我更喜歡這種方式,而不是創建一個新的界面,並將其注入到我的控制器。

+2

這不是做到這一點的最好辦法。依賴注入是更推薦的方法。 – realnero

+0

但是,它是一個適合我的要求嗎?因爲我只需要它來獲取控制器名稱。也許你可以給出一些代碼示例,因爲我認爲它沒有用,只需要傳遞'''Request'''對象就可以創建一個接口。但是再一次,這只是我的看法,如果你能用一些新的信息給我啓發,我會非常感激。 –

+0

它不僅適用於請求對象,還可以封裝其他依賴項,就像您爲ControllerContext所做的一樣。當你的控制器變得複雜它會更容易維護 – realnero

相關問題