13

I am new to MOQ, but am using it with NUnit for unit testing.

I have all parts of my controller mocked, except the following line which throws an 'Object not set to an instance of an object' error message.

Response.Cookies.Clear();

I have the following extension method to mock the controller context which works for everything else I have come accross so far (very much thanks to the good people on this forum).

public static int SetUpForTest(this System.Web.Mvc.Controller ctl, string username, TestingUtility.Roles role)
    {
        var routes = new RouteCollection();
        MvcApplication.RegisterRoutes(routes);

        var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
        request.SetupGet(x => x.ApplicationPath).Returns("/");
        request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
        request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

        var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
        response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny<String>())).Returns((String url) => url);
        // response.SetupGet(x => x.Cookies).Returns(new HttpCookieCollection()); // This also failed to work

        var context = new Mock<HttpContextBase>(MockBehavior.Strict);
        context.SetupGet(x => x.Request).Returns(request.Object);
        context.SetupGet(x => x.Response).Returns(response.Object);
        context.SetupGet(x => x.Response.Cookies).Returns(new HttpCookieCollection()); // still can't call the Clear() method

        //
        // Mock the controller context (using the objects mocked above)
        //
        var moqCtx = new Mock<ControllerContext>(context.Object, new RouteData(), ctl);
        moqCtx.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(username);
        moqCtx.SetupGet(p => p.HttpContext.User.Identity.IsAuthenticated).Returns(true);
        if (!string.IsNullOrEmpty(role.ToString()))
            moqCtx.Setup(p => p.HttpContext.User.IsInRole(role.ToString())).Returns(true);

        //
        // Pass the mocked ControllerContext and create UrlHelper for the controller and return
        //
        ctl.ControllerContext = moqCtx.Object;
        ctl.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
        return 1;
    }

As you can see above I have tried to mock the 'get' of the cookies collection, which does not help.

Also, the actual Clear() method cannot be mocked as it is not a virtual method.

Obviously I don't want to test that the cookies are being cleared, I just want to be able to ignore it in testing.

Thanks,

Greg

tereško
  • 58,060
  • 25
  • 98
  • 150
Greg
  • 165
  • 1
  • 1
  • 7
  • Have you seen this question: http://stackoverflow.com/questions/1228179/mocking-httpcontextbase-with-moq – Halvard Aug 07 '13 at 09:08
  • Thanks for the response, but I have looked through the examples in the link and I do not see anything that I haven't done in the mocking of the controller context above. If I am missing something then please point me in the right direction! – Greg Aug 07 '13 at 09:38
  • Are you sure you are getting the mocked context and not a HttpContext.Current (for example) in that part of the code? Just an idea of what might be wrong. – JuhaKangas Aug 07 '13 at 11:07

3 Answers3

21

This works for me when I do cookies.Clear()

            var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
        request.SetupGet(x => x.ApplicationPath).Returns("/");
        request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
        request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

        var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
        response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny<String>())).Returns((String url) => url);
        // response.SetupGet(x => x.Cookies).Returns(new HttpCookieCollection()); // This also failed to work

        var context = new Mock<HttpContextBase>(MockBehavior.Strict);
        context.SetupGet(x => x.Request).Returns(request.Object);
        context.SetupGet(x => x.Response).Returns(response.Object);
        context.SetupGet(x => x.Response.Cookies).Returns(new HttpCookieCollection()); // still can't call the Clear() method
        context.SetupGet(p => p.User.Identity.Name).Returns("blah");
        context.SetupGet(p => p.User.Identity.IsAuthenticated).Returns(true);

        var rc = new RequestContext(context.Object, new RouteData());

        controller.ControllerContext = new ControllerContext(rc, controller);
Luc Bos
  • 1,722
  • 1
  • 13
  • 24
  • 1
    Thank you - the solution was to only mock the HttpContextBase and not the ControllerContext as you showed in your example. Also thanks to Halvard for his answer pointing me in the right direction – Greg Aug 07 '13 at 13:46
1

Referring previous issue and accurate solution of it without using ActionContext: How to unit test a Controller action using the Response property in ASP.NET 5 (MVC 6)? [duplicate]

Mocking HttpResponse, HttpContext are difficult as they are only read-only type. So, to mock these use SetupGet on HttpResponse and HttpContext, and then pass HttpContext reference to ControllerContext.

    var headerDict = new HeaderDictionary();
    var httpResponse = new Mock<HttpResponse>();
    var httpContext = new Mock<HttpContext>();
    
    
    httpRespnse.SetupGet(=>c.Headers).Returns(headerDict);
    httpContext.SetupGet(=>c.Response).Returns(httpResponse.Object);

    var controllerContext = new ControllerContext()
   {
       HttpContext = httpContext.Object
   };

    var controllerInstance = new ValueController()
   {
       ControllerContext = controllerContext
   };
0

(This is only half an answer, but was too big for the comment field ...)

Your mocking of new HttpCookieCollection() is correct. This code works in separation:

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny<String>())).Returns((String url) => url);
// response.SetupGet(x => x.Cookies).Returns(new HttpCookieCollection()); // This also failed to work

var context = new Mock<HttpContextBase>(MockBehavior.Strict);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
context.SetupGet(x => x.Response.Cookies).Returns(new HttpCookieCollection()); // still can't call the Clear() method


// Here clearing the cookies works just fine:
var instance = context.Object;
instance.Response.Cookies.Clear();

So the error is not there, but somewhere else. What happens if you comment out the line with Response.Cookies.Clear() from your code? Is everything else mocked correctly then? When you debug the test, can you see that the rest of the mock is as expected? I would be surprised if it was (but I have been surprised before...).

Halvard
  • 3,891
  • 6
  • 39
  • 51