在单元测试中模拟 IHttpContextAccessor
Posted
技术标签:
【中文标题】在单元测试中模拟 IHttpContextAccessor【英文标题】:Mock IHttpContextAccessor in Unit Tests 【发布时间】:2018-11-27 17:49:10 【问题描述】:我有一种使用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】:您可以使用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);
【讨论】:
【参考方案2】:在我的场景中,我必须模拟 IHttpContextAccessor
并访问内部请求 url 位。
我在这里分享它是因为我花了相当多的时间来解决这个问题,希望它会对某人有所帮助。
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;
【讨论】:
_HttpContextAccessor.SetupGet(accessor => accessor.HttpContext).Returns(httpContext);
以上是关于在单元测试中模拟 IHttpContextAccessor的主要内容,如果未能解决你的问题,请参考以下文章