Mock IHttpContextAccessor in Unit Tests

28,313

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;
  }
}
Share:
28,313
superninja
Author by

superninja

Updated on July 08, 2022

Comments

  • superninja
    superninja almost 2 years

    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?

  • Shimmy Weitzhandler
    Shimmy Weitzhandler over 3 years
    _HttpContextAccessor.SetupGet(accessor => accessor.HttpContext).Returns(httpContext);