Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpContext.Current is null when unit test

I have following web Api controller method.

When I run this code through web, HttpContext.Current is never null and give desired value.

public override void Post([FromBody]TestDTO model)
{

    var request = HttpContext.Current.Request;
    var testName = request.Headers.GetValues("OS Type")[0];
    // more code

}

However, when I call this method from Unit Test, HttpContext.Current is always null.

How do i fix it?

like image 723
simbada Avatar asked Jul 03 '16 13:07

simbada


People also ask

Why HttpContext current is null?

Current is not null only if you access it in a thread that handles incoming requests. That's why it works "when i use this code in another class of a page".

How do I find HttpContext current?

If you're writing custom middleware for the ASP.NET Core pipeline, the current request's HttpContext is passed into your Invoke method automatically: public Task Invoke(HttpContext context) { // Do something with the current HTTP context... }

What is HttpContext current in C#?

The property stores the HttpContext instance that applies to the current request. The properties of this instance are the non-static properties of the HttpContext class. You can also use the Page. Context property to access the HttpContext object for the current HTTP request.

What is the use of HttpContext current?

HttpContext is an object that wraps all http related information into one place. HttpContext. Current is a context that has been created during the active request. Here is the list of some data that you can obtain from it.


2 Answers

During unit tests HttpContext is always null as it is usually populate by IIS. You have a few options around this.

Sure, you could mock the HttpContext, (which you shouldn't really do - Don't mock HttpContext!!!! He doesn't like to be mocked!),. You should really try to stay away from tight coupling with HttpContext all over your code. Try constraining it to one central area (SRP);

Instead figure out what is the functionality you would like to achieve and design an abstraction around that. This will allow for your code to be more testable as it is not so tightly coupled to HttpContext.

Based on your example you are looking to access header values. This is just an example of how to change your thinking when it comes to using HttpContext.

Your original example has this

var request = HttpContext.Current.Request;
var testName = request.Headers.GetValues("OS Type")[0];

When you are looking for something like this

var testName = myService.GetOsType();

Well then create a service that provides that

public interface IHeaderService {
    string GetOsType();
}

which could have a concrete implementation like

public class MyHeaderService : IHeaderService {

    public string GetOsType() {
        var request = HttpContext.Current.Request;
        var testName = request.Headers.GetValues("OS Type")[0];
        return testName;
    }
}

Now in your controller you can have your abstraction instead of having tight coupling to HttpContext

public class MyApiController : ApiController {
    IHeaderService myservice;
    public MyApiController(IHeaderService headers) {
        myservice = headers;
    }

    public IHttpActionResult Post([FromBody]TestDTO model) {    
        var testName = myService.GetOsType();
        // more code

    }    
}

You can later inject your concrete type to get the functionality you want.

For testing you then swap dependencies to run your test.

If the method under test is your Post() method you can create a fake dependency or use a mocking framework

[TestClass]
public class MyTestClass {

    public class MyFakeHeaderService : IHeaderService {
        string os;
        public MyFakeHeaderService(string os) {
            this.os = os;
        }

        public string GetOsType() {
            return os;
        }
    }

    [TestMethod]
    public void TestPostMethod() {
        //Arrange
        IHeaderService headers = new MyFakeHeaderService("FAKE OS TYPE");
        var sut = new MyApiController(headers);
        var model = new TestDTO();

        //Act
        sut.Post(model);

        //Assert
        //.....
    }
}
like image 77
Nkosi Avatar answered Nov 07 '22 14:11

Nkosi


This is by design and it's always null. But there is a FakeHttpContext project on Nuget that simply you can use it.

To install FakeHttpContext, run the following command in the Package Manager Console (PMC)

Install-Package FakeHttpContext 

And then use it like this:

using (new FakeHttpContext())
{
HttpContext.Current.Session["mySession"] = "This is a test";       
}

Visit https://www.nuget.org/packages/FakeHttpContext to install the package

See examples on Github: https://github.com/vadimzozulya/FakeHttpContext#examples

Hope this will help :)

like image 27
Ahmad Payan Avatar answered Nov 07 '22 14:11

Ahmad Payan