return IActionResult 的单元测试控制器方法
Unit testing controller methods which return IActionResult
我正在构建一个 ASP.NET 核心 WebAPI,我正在尝试为控制器编写单元测试。我发现的大多数示例都来自较旧的 WebAPI/WebAPI2 平台,似乎与新的 Core 控制器无关。
我的控制器方法正在返回 IActionResults
。但是,IActionResult
对象只有一个 ExecuteResultAsync()
方法需要控制器上下文。我是手动实例化controller,所以这个实例的controller context为null,调用ExecuteResultAsync
时会出现异常。从本质上讲,这使我走上了一条非常棘手的道路,以使这些单元测试成功完成并且非常混乱。我想知道必须有更多 simple/correct 的方法来测试 API 控制器。
此外,我的控制器没有使用 async/await 如果这会有所不同。
我正在努力实现的简单示例:
控制器方法:
[HttpGet(Name = "GetOrdersRoute")]
public IActionResult GetOrders([FromQuery]int page = 0)
{
try
{
var query = _repository.GetAll().ToList();
int totalCount = query.Count;
int totalPages = (int)Math.Ceiling((double)totalCount / pageSize) - 1;
var orders = query.Skip(pageSize * page).Take(pageSize);
return Ok(new
{
TotalCount = totalCount,
TotalPages = totalPages,
Orders = orders
});
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
单元测试:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk()
{
// arrange
var controller = new OrdersController(new MockRepository());
// act
IActionResult result = controller.GetOrders();
// assert
Assert.Equal(HttpStatusCode.OK, ????);
}
假设类似
public IActionResult GetOrders() {
var orders = repository.All();
return Ok(orders);
}
本例中的控制器返回 OkObjectResult
class。
将结果转换为您在方法中返回的类型并对其执行断言
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
var okResult = result as OkObjectResult;
// assert
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
您还可以做一些很酷的事情,例如:
var result = await controller.GetOrders();//
var okResult = result as ObjectResult;
// assert
Assert.NotNull(okResult);
Assert.True(okResult is OkObjectResult);
Assert.IsType<TheTypeYouAreExpecting>(okResult.Value);
Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode);
谢谢
您还可以使用 ActionResult class 作为控制器结果(假设您有 Orders 类型)。
在这种情况下,你可以使用这样的东西:
[ProducesResponseType(typeof(Orders), StatusCodes.Status200OK)]
public ActionResult<Orders> GetOrders()
{
return service.GetOrders();
}
现在在单元测试中你有:
Assert.IsInstanceOf<Orders>(result.Value);
此外,这是微软的推荐 - https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.2#actionresultt-type
不幸的是,我不知道为什么要使用Ok方法
return Ok(service.GetOrders());
未正确映射。
建议转换为 ObjectResult
的其他答案,但只有当您 return OkObjectResult
\ NotFoundObjectResult
\ 等时它才有效。但是服务器可以 return NotFound
\ OkResult
派生自 StatusCodeResult
。
例如:
public class SampleController : ControllerBase
{
public async Task<IActionResult> FooAsync(int? id)
{
if (id == 0)
{
// returned "NotFoundResult" base type "StatusCodeResult"
return NotFound();
}
if (id == 1)
{
// returned "StatusCodeResult" base type "StatusCodeResult"
return StatusCode(StatusCodes.Status415UnsupportedMediaType);
}
// returned "OkObjectResult" base type "ObjectResult"
return new OkObjectResult("some message");
}
}
查看了所有这些方法的实现,发现都是继承自IStatusCodeActionResult
接口。看起来这是包含 StatusCode
:
的最基本类型
private SampleController _sampleController = new SampleController();
[Theory]
[InlineData(0, StatusCodes.Status404NotFound)]
[InlineData(1, StatusCodes.Status415UnsupportedMediaType)]
[InlineData(2, StatusCodes.Status200OK)]
public async Task Foo_ResponseTest(int id, int expectedCode)
{
var actionResult = await _sampleController.FooAsync(id);
var statusCodeResult = (IStatusCodeActionResult)actionResult;
Assert.Equal(expectedCode, statusCodeResult.StatusCode);
}
这样做的一个好方法是这样的:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
// assert
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
public async Task CallRxData_ReturnsHttpNotFound_ForInvalidJobNum_ReturnsStoredRxOrder()
{
var scanInController = new ScanInController(_logger, _scanInService);
var okResult = await scanInController.CallRxData(rxOrderRequest);
var notFoundResult = await scanInController.CallRxData(invalidRxOrderRequest);
var okResultWithScanInCheckFalse = await scanInController.CallRxData(rxOrderRequest);
var okResultWithEmptyAelAntiFakeDatas = await scanInController.CallRxData(rxOrderRequest);
// Assert
Assert.That(okResult, Is.TypeOf<OkObjectResult>());
Assert.That(notFoundResult, Is.TypeOf<NotFoundObjectResult>());
Assert.IsFalse(((okResultWithScanInCheckFalse as ObjectResult).Value as RxOrder).IsSecurity);`enter code here`
}
我正在构建一个 ASP.NET 核心 WebAPI,我正在尝试为控制器编写单元测试。我发现的大多数示例都来自较旧的 WebAPI/WebAPI2 平台,似乎与新的 Core 控制器无关。
我的控制器方法正在返回 IActionResults
。但是,IActionResult
对象只有一个 ExecuteResultAsync()
方法需要控制器上下文。我是手动实例化controller,所以这个实例的controller context为null,调用ExecuteResultAsync
时会出现异常。从本质上讲,这使我走上了一条非常棘手的道路,以使这些单元测试成功完成并且非常混乱。我想知道必须有更多 simple/correct 的方法来测试 API 控制器。
此外,我的控制器没有使用 async/await 如果这会有所不同。
我正在努力实现的简单示例:
控制器方法:
[HttpGet(Name = "GetOrdersRoute")]
public IActionResult GetOrders([FromQuery]int page = 0)
{
try
{
var query = _repository.GetAll().ToList();
int totalCount = query.Count;
int totalPages = (int)Math.Ceiling((double)totalCount / pageSize) - 1;
var orders = query.Skip(pageSize * page).Take(pageSize);
return Ok(new
{
TotalCount = totalCount,
TotalPages = totalPages,
Orders = orders
});
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
单元测试:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk()
{
// arrange
var controller = new OrdersController(new MockRepository());
// act
IActionResult result = controller.GetOrders();
// assert
Assert.Equal(HttpStatusCode.OK, ????);
}
假设类似
public IActionResult GetOrders() {
var orders = repository.All();
return Ok(orders);
}
本例中的控制器返回 OkObjectResult
class。
将结果转换为您在方法中返回的类型并对其执行断言
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
var okResult = result as OkObjectResult;
// assert
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
您还可以做一些很酷的事情,例如:
var result = await controller.GetOrders();//
var okResult = result as ObjectResult;
// assert
Assert.NotNull(okResult);
Assert.True(okResult is OkObjectResult);
Assert.IsType<TheTypeYouAreExpecting>(okResult.Value);
Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode);
谢谢
您还可以使用 ActionResult class 作为控制器结果(假设您有 Orders 类型)。 在这种情况下,你可以使用这样的东西:
[ProducesResponseType(typeof(Orders), StatusCodes.Status200OK)]
public ActionResult<Orders> GetOrders()
{
return service.GetOrders();
}
现在在单元测试中你有:
Assert.IsInstanceOf<Orders>(result.Value);
此外,这是微软的推荐 - https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.2#actionresultt-type
不幸的是,我不知道为什么要使用Ok方法
return Ok(service.GetOrders());
未正确映射。
建议转换为 ObjectResult
的其他答案,但只有当您 return OkObjectResult
\ NotFoundObjectResult
\ 等时它才有效。但是服务器可以 return NotFound
\ OkResult
派生自 StatusCodeResult
。
例如:
public class SampleController : ControllerBase
{
public async Task<IActionResult> FooAsync(int? id)
{
if (id == 0)
{
// returned "NotFoundResult" base type "StatusCodeResult"
return NotFound();
}
if (id == 1)
{
// returned "StatusCodeResult" base type "StatusCodeResult"
return StatusCode(StatusCodes.Status415UnsupportedMediaType);
}
// returned "OkObjectResult" base type "ObjectResult"
return new OkObjectResult("some message");
}
}
查看了所有这些方法的实现,发现都是继承自IStatusCodeActionResult
接口。看起来这是包含 StatusCode
:
private SampleController _sampleController = new SampleController();
[Theory]
[InlineData(0, StatusCodes.Status404NotFound)]
[InlineData(1, StatusCodes.Status415UnsupportedMediaType)]
[InlineData(2, StatusCodes.Status200OK)]
public async Task Foo_ResponseTest(int id, int expectedCode)
{
var actionResult = await _sampleController.FooAsync(id);
var statusCodeResult = (IStatusCodeActionResult)actionResult;
Assert.Equal(expectedCode, statusCodeResult.StatusCode);
}
这样做的一个好方法是这样的:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
// assert
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
public async Task CallRxData_ReturnsHttpNotFound_ForInvalidJobNum_ReturnsStoredRxOrder()
{
var scanInController = new ScanInController(_logger, _scanInService);
var okResult = await scanInController.CallRxData(rxOrderRequest);
var notFoundResult = await scanInController.CallRxData(invalidRxOrderRequest);
var okResultWithScanInCheckFalse = await scanInController.CallRxData(rxOrderRequest);
var okResultWithEmptyAelAntiFakeDatas = await scanInController.CallRxData(rxOrderRequest);
// Assert
Assert.That(okResult, Is.TypeOf<OkObjectResult>());
Assert.That(notFoundResult, Is.TypeOf<NotFoundObjectResult>());
Assert.IsFalse(((okResultWithScanInCheckFalse as ObjectResult).Value as RxOrder).IsSecurity);`enter code here`
}