I have a method to get header value using IHttpContextAccessor
public class HeaderConfiguration : IHeaderConfiguration { public HeaderConfiguration() { } public string GetTenantId(IHttpContextAccessor httpContextAccessor) { return httpContextAccessor.HttpContext.Request.Headers["Tenant-ID"].ToString(); } }
I am testing GetBookByBookId
method
Let's say the method looks like this:
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 //... } }
Here's my unit test for GetBookByBookId
method
[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>>(); }
But for this line:
mockHttpContextAccessor.Setup(req => req.HttpContext.Request.Headers["Tenant-ID"].ToString()).Returns(It.IsAny<string>());
It says
System.NotSupportedException: 'Type to mock must be an interface or an abstract or non-sealed class. '
I was wondering what's the proper way to mock IHttpContextAccessor
with header value?
It's only necessary to use IHttpContextAccessor when you need access to the HttpContext inside a service.
ASP.NET Core applications access the HTTPContext through the IHttpContextAccessor interface. The HttpContextAccessor class implements it. You can use this class when you need to access HttpContext inside a service.
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
You can use the DefaultHttpContext
as a backing for the IHttpContextAccessor.HttpContext
. Saves you having to set-up too many things
Next you cannot use It.IsAny<string>()
as a Returns
result. They were meant to be used in the set up expressions alone.
Check the refactor
[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>>(); }
There may also be an issue with the Class Under Test as it is manually initializing the HeaderConfiguration
when it should actually be explicitly injected.
public Book(IHeaderConfiguration headerConfiguration, IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; _tenantID = headerConfiguration.GetTenantId(_httpContextAccessor); }
In my scenario I had to mock IHttpContextAccessor
and access the inner request url bits.
I'm sharing it here because I spent a decent amount of time figuring this out and hopefully it'll help someone.
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With