创建控制器时的可测性考虑因素

时间:2017-06-05 00:25:41

标签: c# asp.net unit-testing asp.net-web-api

我正在Web API 2中编写控制器,对其执行odata查询:

[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount()
{
    var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
    var response = _accountService.Get(query);

    if (!response.Result.IsSuccessStatusCode)
    {
        return NotFound();
    }

    var readAsAsync = response.Result.Content.ReadAsAsync<object>();
    if (readAsAsync == null)
    {
        return NotFound();
    }

    var result = await readAsAsync;
    return Ok(result);
} 

如何注入Request,特别是与var query = Request.RequestUri.PathAndQuery.Split('/')[2];相关的内容?

这是我为这个控制器编写的一个非常基本的测试:

[TestMethod]
public void GetAccount_Returns_IHttpActionResultTask()
{
    var accountsService = new Mock<IAccountService>();
    var sut = new AccountsController(accountsService.Object);

    Assert.IsInstanceOfType(sut.GetAccount(), typeof(Task<IHttpActionResult>));
}

为了使用Request.RequestUri ....的不同值进行测试,如何重写我的控制器以使其更易于测试?

1 个答案:

答案 0 :(得分:2)

Request上设置ApiCntroller属性。

[TestMethod]
public async Task GetAccount_Returns_IHttpActionResult() {
    //Arrange
    var accountsService = new Mock<IAccountService>();
    var sut = new AccountsController(accountsService.Object);
    sut.Request = new HttpRequestMessage {
        RequestUri = new Uri("http://localhost/api/accounts?filter=name")
    };

    //Act
    var result = await sut.GetAccount();

    //Assert
    Assert.IsInstanceOfType(result, typeof(IHttpActionResult));
}

此外,测试方法还存在潜在的阻塞问题。混合异步/等待与.Result阻塞调用可能会导致死锁。

重构:

[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount() {
    var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
    var response = await _accountService.Get(query);

    if (!response.IsSuccessStatusCode) {
        return NotFound();
    }

    var readAsAsync = response.Content.ReadAsAsync<object>();
    if (readAsAsync == null) {
        return NotFound();
    }

    var result = await readAsAsync;
    return Ok(result);
}