面向云技术架构 - 痴者工良

  • 首页
  • 工良写的电子书
    • kubernetes 教程
    • 从 C# 入门 Kafka
    • 多线程和异步
    • 动态编程-反射、特性、AOP
    • 表达式树
  • 本站文章导航
  • 隐私政策
愿有人陪你颠沛流离
遇到能让你付出的事物或者人,都是一种运气。
能遇到,就该珍惜。或许你们最终没能在一起,但你会切实地感受到力量。
正因为这样,那段相遇才变得有价值,才没有辜负这世间的每一段相遇。
  1. 首页
  2. .NET
  3. 正文

控制器单元测试示例

2022年3月30日 610点热度 0人点赞 0条评论
内容纲要

使用接口的另一个好处是,方便 mock。
要检验自己代码是否好,足够分离,是否过度设计等,给自己写完整的单元测试。
如果自己写的代码,单元测试很难写或者无从下手,那么就要考虑改进代码了。
file

首先在单元测试类中,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();
        }
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: 单元测试 控制器 示例
最后更新:2022年5月28日

痴者工良

高级程序员劝退师

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 whuanle.cn. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

粤ICP备18051778号

粤公网安备 44030902003257号