代码之家  ›  专栏  ›  技术社区  ›  superninja

单元测试中的模拟ihttpContextAccessor

  •  11
  • superninja  · 技术社区  · 6 年前

    我有一个方法可以使用 IHttpContextAccessor

    public class HeaderConfiguration : IHeaderConfiguration
    {
        public HeaderConfiguration()
        {
    
        }
    
        public string GetTenantId(IHttpContextAccessor httpContextAccessor)
        {
            return httpContextAccessor.HttpContext.Request.Headers["Tenant-ID"].ToString();
        }
    }
    

    我在测试 GetBookByBookId 方法

    假设方法如下:

    public class Book
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private IHeaderConfiguration _headerConfiguration;
        private string _tenantID;
    
        public Book(IHeaderConfiguration headerConfiguration, IHttpContextAccessor httpContextAccessor){
            var headerConfig = new HeaderConfiguration();
            _httpContextAccessor = httpContextAccessor;
            _tenantID = headerConfig.GetTenantId(_httpContextAccessor);
        }
    
        public Task<List<BookModel>> GetBookByBookId(string id){
            //do something with the _tenantId
            //...
        }
    }
    

    这是我的单元测试 GetBookByBookID 方法

    [Fact]
    public void test_GetBookByBookId()
    {
        //Arrange
    
        //Mock IHttpContextAccessor
        var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
    
        mockHttpContextAccessor.Setup(req => req.HttpContext.Request.Headers["Tenant-ID"].ToString()).Returns(It.IsAny<string>());
        //Mock HeaderConfiguration
        var mockHeaderConfiguration = new Mock<IHeaderConfiguration>();
        mockHeaderConfiguration.Setup(x => x.GetTenantId(mockHttpContextAccessor.Object)).Returns(It.IsAny<string>());
    
        var book = new Book( mockHttpContextAccessor.Object, mockHeaderConfiguration.Object);
    
        var bookId = "100";
    
        //Act
        var result = book.GetBookByBookId(bookId);
    
        //Assert
        result.Result.Should().NotBeNull().And.
            BeOfType<List<BookModel>>();
    }
    

    但对于这一行:

    mockHttpContextAccessor.Setup(req => req.HttpContext.Request.Headers["Tenant-ID"].ToString()).Returns(It.IsAny<string>());
    

    它说

    system.notsupportedexception:“要模拟的类型必须是接口、抽象类或非密封类。”

    我在想什么是嘲笑的正确方法 IHttpContextAccessor接口 有头值吗?

    1 回复  |  直到 6 年前
        1
  •  23
  •   Nkosi    6 年前

    你可以使用 DefaultHttpContext 作为后盾 IHttpContextAccessor.HttpContext . 省得你设置太多东西

    接下来你不能使用 It.IsAny<string>() 作为一个 Returns 结果。它们本应单独用于设置表达式。

    检查重构

    [Fact]
    public async Task test_GetBookByBookId() {
        //Arrange
    
        //Mock IHttpContextAccessor
        var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
        var context = new DefaultHttpContext();
        var fakeTenantId = "abcd";
        context.Request.Headers["Tenant-ID"] = fakeTenantId;
        mockHttpContextAccessor.Setup(_ => _.HttpContext).Returns(context);
        //Mock HeaderConfiguration
        var mockHeaderConfiguration = new Mock<IHeaderConfiguration>();
        mockHeaderConfiguration
            .Setup(_ => _.GetTenantId(It.IsAny<IHttpContextAccessor>()))
            .Returns(fakeTenantId);
    
        var book = new Book(mockHttpContextAccessor.Object, mockHeaderConfiguration.Object);
    
        var bookId = "100";
    
        //Act
        var result = await book.GetBookByBookId(bookId);
    
        //Assert
        result.Should().NotBeNull().And.
            BeOfType<List<BookModel>>();
    }
    

    测试中的类也可能有问题,因为它正在手动初始化 HeaderConfiguration 什么时候应该明确注入。

    public Book(IHeaderConfiguration headerConfiguration, IHttpContextAccessor httpContextAccessor) {
        _httpContextAccessor = httpContextAccessor;
        _tenantID = headerConfiguration.GetTenantId(_httpContextAccessor);
    }