Mock IHttpContextAccessor in Unit Tests
我有一种使用
1 2 3 4 5 6 7 8 9 10 11 12 | public class HeaderConfiguration : IHeaderConfiguration { public HeaderConfiguration() { } public string GetTenantId(IHttpContextAccessor httpContextAccessor) { return httpContextAccessor.HttpContext.Request.Headers["Tenant-ID"].ToString(); } } |
我正在测试
假设方法看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 //... } } |
这是我对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | [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>>(); } |
但是对于这一行:
1 | mockHttpContextAccessor.Setup(req => req.HttpContext.Request.Headers["Tenant-ID"].ToString()).Returns(It.IsAny<string>()); |
它说
System.NotSupportedException: 'Type to mock must be an interface or an abstract or non-sealed class. '
我想知道使用标头值模拟
您可以将
接下来,您不能将
检查重构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | [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>>(); } |
由于实际上应该显式注入
1 2 3 4 | public Book(IHeaderConfiguration headerConfiguration, IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; _tenantID = headerConfiguration.GetTenantId(_httpContextAccessor); } |
在我的场景中,我不得不模拟
我在这里分享是因为我花了很多时间弄清楚这个问题,希望对您有所帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | readonly Mock<IHttpContextAccessor> _HttpContextAccessor = new Mock<IHttpContextAccessor>(MockBehavior.Strict); void SetupHttpContextAccessorWithUrl(string currentUrl) { var httpContext = new DefaultHttpContext(); setRequestUrl(httpContext.Request, currentUrl); _HttpContextAccessor .SetupGet(accessor => accessor.HttpContext) .Returns(httpContext); static void setRequestUrl(HttpRequest httpRequest, string url) { UriHelper .FromAbsolute(url, out var scheme, out var host, out var path, out var query, fragment: out var _); httpRequest.Scheme = scheme; httpRequest.Host = host; httpRequest.Path = path; httpRequest.QueryString = query; } } |