0

I am starting to write unit tests for all our controllers and seem to be getting the hang of it, but now i am a bit stuck. I have the following controller method that i would like to write a unit test for but a am a bit lost. Can someone please help and point me in the right direction. I am guessing maybe i need to abstract the method slightly, but not sure how.

public async Task<IHttpActionResult> PostAsync()
{
    if (HttpContext.Current.Request.Files.AllKeys.Any())
    {
        // Get the uploaded image from the Files collection
        var httpPostedFile = HttpContext.Current.Request.Files[0];

        if (httpPostedFile != null)
        {
            // Validate the uploaded image, by only accepting certain file types and sizes
            var validExtensions = new List<string>
            {
                ".JPG", ".JPE", ".BMP", ".GIF", ".PNG"
            };

            if (!validExtensions.Contains(Path.GetExtension(httpPostedFile.FileName).ToUpperInvariant()))
            {
                return BadRequest();
            }
            else if (httpPostedFile.ContentLength > 2097152)
            {
                // file is over 2mb in size
                return BadRequest();
            }

            // create a new image
            var entity = new Image
            {
                Name = httpPostedFile.FileName,
                Size = httpPostedFile.ContentLength,
                Data = new ImageData
                {
                    Content = new byte[httpPostedFile.ContentLength]   
                }
            };

            await httpPostedFile.InputStream.ReadAsync(entity.Data.Content, 0, httpPostedFile.ContentLength);

            await _service.AddAsync(entity);

            return Created<ImageModel>(Request.RequestUri, Mapper.Map<ImageModel>(entity));
        }
    }

    return BadRequest();

}

EDIT:

Sorry i completely forgot to include dependancy injection code. I am using SimpleInjector.

So i have now added this to it

// return current httpContext
container.RegisterPerWebRequest<HttpContext>(() => HttpContext.Current);

Which i cannot yet test because i still cannot work out how to mock httpContext. my controller is now created like so

private IImageService _service;
private HttpContext _httpContext;

public ImageController(IImageService service, HttpContext httpContext)
{
    _service = service;
    _httpContext = httpContext;
}

And i have changed HttpContext.Current to _httpContext.

But how can i create a mock of HttpContext??

Gillardo
  • 8,447
  • 14
  • 60
  • 126

1 Answers1

1

Assuming you're using dependency injection, the best thing you can do to make this method testable is to inject the HttpContext.Current, or even the Request, as a dependency to your controller.

public class MyController
{
    private readonly IMyService _service;
    private readonly HttpContext _httpContext;

    public MyController(IMyService service, HttpContext httpContext)
    {
        _service = service;
        _httpContext = httpContext;
    }

    public async Task<IHttpActionResult> PostAsync()
    {
        // action method content
    }
}

Then update your action method to use _httpContext instead of the static HttpContext.Current. Doing this will allow you to create a mock HttpContext in your test and pass it into your controller, giving you control over it.

From here, set up your mock HttpContext to return whatever you need for your test for parts like _httpContext.Request.Files.AllKeys.Any() and var httpPostedFile = _httpContext.Request.Files[0];.

You will also need to set up your IoC container (Ninject, Autofac, or whatever you're using) to inject the correct instance of HttpContext/Request into your controller.

This should give you a good place to start, since going into more detail would require knowledge of which mocking framework, IoC container, etc you're using :)


Update

Mocking HttpContext is tricky, but I've had success mocking it directly in the past. The following are some methods I wrote that use NSubstitute to mock HttpContext as an HttpContextBase, along with mock HttpRequestBase and HttpResponseBase properties.

public static class MockHttpObjectBuilder
{
    public static HttpContextBase GetMockHttpContext()
    {
        return GetMockHttpContext("~/");
    }

    public static HttpContextBase GetMockHttpContext(string url, string httpMethod = "GET")
    {
        var context = Substitute.For<HttpContextBase>();

        var req = GetMockHttpRequest(url, httpMethod);
        req.RequestContext.Returns(new RequestContext(context, new RouteData()));
        context.Request.Returns(req);

        var res = GetMockHttpResponse();
        context.Response.Returns(res);

        return context;
    }

    public static HttpRequestBase GetMockHttpRequest()
    {
        return GetMockHttpRequest("~/");
    }

    public static HttpRequestBase GetMockHttpRequest(string url, string httpMethod = "GET")
    {
        var req = Substitute.For<HttpRequestBase>();
        req.ApplicationPath.Returns("/");
        req.Headers.Returns(new NameValueCollection {{"X-Cluster-Client-Ip", "1.2.3.4"}});
        req.ServerVariables.Returns(new NameValueCollection());
        req.AppRelativeCurrentExecutionFilePath.Returns(url);
        req.HttpMethod.Returns(httpMethod);
        req.Url.Returns(new Uri("example.com/" + url.TrimStart('~')));
        return req;
    }

    public static HttpResponseBase GetMockHttpResponse()
    {
        var res = Substitute.For<HttpResponseBase>();
        res.ApplyAppPathModifier(Arg.Any<string>()).Returns(x => x[0]);
        return res;
    }
}

You may not need all of the things I'm mocking here, this was just a copy/paste of some code I wrote a while ago. Also, you may have to play around with using HttpContext vs HttpContextBase in your signatures. Here are some links that may help:

Community
  • 1
  • 1
Jim Skerritt
  • 3,769
  • 2
  • 25
  • 22