Mock IHttpContextAccessor in Unit Tests

C#Unit Testingasp.net CoreMoqasp.net Core-Webapi

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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);
}

Solution 2 - C#

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;
  }
}

Solution 3 - C#

If you are making use of the wonderful NSubstitute package for NUnit, you can do this...

        var mockHttpAccessor = Substitute.For<IHttpContextAccessor>();
        var context = new DefaultHttpContext
        {
            Connection =
            {
                Id = Guid.NewGuid().ToString()
            }
        };
        
        mockHttpAccessor.HttpContext.Returns(context);
        
        // usage...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsuperninjaView Question on Stackoverflow
Solution 1 - C#NkosiView Answer on Stackoverflow
Solution 2 - C#Shimmy WeitzhandlerView Answer on Stackoverflow
Solution 3 - C#SludgedogView Answer on Stackoverflow