内容目录
使用接口的另一个好处是,方便 mock。
要检验自己代码是否好,足够分离,是否过度设计等,给自己写完整的单元测试。
如果自己写的代码,单元测试很难写或者无从下手,那么就要考虑改进代码了。
首先在单元测试类中,mock 注入服务:
public class BasketWebApiTest
{
private readonly Mock<IBasketRepository> _basketRepositoryMock;
private readonly Mock<IBasketIdentityService> _identityServiceMock;
private readonly Mock<IEventBus> _serviceBusMock;
private readonly Mock<ILogger<BasketController>> _loggerMock;
public BasketWebApiTest()
{
_basketRepositoryMock = new Mock<IBasketRepository>();
_identityServiceMock = new Mock<IBasketIdentityService>();
_serviceBusMock = new Mock<IEventBus>();
_loggerMock = new Mock<ILogger<BasketController>>();
}
}
然后开始真正 mock 一个方法和参加 Controller 并测试。
[Fact]
public async Task Get_customer_basket_success()
{
//Arrange
var fakeCustomerId = "1";
var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId);
_basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny<string>()))
.Returns(Task.FromResult(fakeCustomerBasket));
_identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId);
_serviceBusMock.Setup(x => x.Publish(It.IsAny<UserCheckoutAcceptedIntegrationEvent>()));
//Act
var basketController = new BasketController(
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId);
//Assert
Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId);
}
也可以 mock 函数的参数:
_basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny<ApplicationUser>(), It.IsAny<Int32>()))
.Returns(Task.FromResult(1));
如果正常注入服务,直接 new Controller
即可,但是如果 Controller 依赖于 HttpContext(这个可以不通过依赖注入使用),因为可以通过 ControllerBase 继承而来。
[Controller]
public abstract class ControllerBase
{
public HttpContext HttpContext
public HttpRequest Request
public HttpResponse Response
public RouteData RouteData
public ModelStateDictionary ModelState
[ControllerContext]
public ControllerContext ControllerContext
public IModelMetadataProvider MetadataProvider
public IModelBinderFactory ModelBinderFactory
public IUrlHelper Url
public IObjectModelValidator ObjectValidator
public ProblemDetailsFactory ProblemDetailsFactory
public ClaimsPrincipal User
那么除了 mock 注入的服务,还需要 mock ControllerBase 中的一些属性对象。
//Act
var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object);
orderController.ControllerContext.HttpContext = _contextMock.Object;
var actionResult = await orderController.AddToCart(fakeCatalogItem);
单元测试的名字可以长一些:
public async Task Get_get_all_catalogitems_and_response_ok_status_code()
public async Task Get_get_catalogitem_by_id_and_response_ok_status_code()
public async Task Get_get_catalogitem_by_id_and_response_bad_request_status_code()
public async Task Get_get_catalogitem_by_id_and_response_not_found_status_code()
public async Task Get_get_catalogitem_by_name_and_response_ok_status_code()
public async Task Get_get_paginated_catalogitem_by_name_and_response_ok_status_code()
如果只是测试会不会出现错误,而不检查返回值,则这样写最简洁:
using (var server = CreateServer())
{
var response = await server.CreateClient()
.GetAsync("api/v1/catalog/catalogtypes");
response.EnsureSuccessStatusCode();
}
文章评论