views:

527

answers:

3

I'm working on an ASP.NET MVC application, and am trying to write some unit tests against controller actions, some of which manipulate properties on the HttpContext, such as Session, Request.Cookies, Response.Cookies, etc. I'm having some trouble figuring out how to "Arrange, Act, Assert"...I can see Arrange and Assert...but I'm having trouble figuring out how to "Act" on properties of a mocked HttpContextBase when all of its properties only have getters. I can't set anything on my mocked context from within my controller actions...so it doesn't seem very useful. I'm fairly new to mocking, so I'm sure there's something that I'm missing, but it seems logical to me that I should be able to create a mock object that I can use in the context of testing controller actions where I can actually set property values, and then later Assert that they're still what I set them to, or something like that. What am I missing?

    public static HttpContextBase GetMockHttpContext()
    {
        var requestCookies = new Mock<HttpCookieCollection>();
        var request = new Mock<HttpRequestBase>();
        request.Setup(r => r.Cookies).Returns(requestCookies.Object);
        request.Setup(r => r.Url).Returns(new Uri("http://example.org"));

        var responseCookies = new Mock<HttpCookieCollection>();
        var response = new Mock<HttpResponseBase>();
        response.Setup(r => r.Cookies).Returns(responseCookies.Object);

        var context = new Mock<HttpContextBase>();

        context.Setup(ctx => ctx.Request).Returns(request.Object);
        context.Setup(ctx => ctx.Response).Returns(response.Object);
        context.Setup(ctx => ctx.Session).Returns(new Mock<HttpSessionStateBase>().Object);
        context.Setup(ctx => ctx.Server).Returns(new Mock<HttpServerUtilityBase>().Object);
        context.Setup(ctx => ctx.User).Returns(GetMockMembershipUser());
        context.Setup(ctx => ctx.User.Identity).Returns(context.Object.User.Identity);
        context.Setup(ctx => ctx.Response.Output).Returns(new StringWriter());

        return context.Object;
    }
A: 

Check out this article: http://hadihariri.com/blogengine/post/2009/03/30/Mocking-UserAgent-property-in-ASPNET-MVC-with-Moq.aspx

Sergey
I read the article. Unfortunately, it didn't help much. It demonstrates how to use the mock framework to "Arrange" what the mock should return. I know how to do that. My problem is, the controller actions that I'm trying to test *actually sets* a property on the injected HttpContext object. When I provide it a mock context object...that action fails, because the mock context object properties have no setters. I don't want to *tell* it what to return...I want to give it a context, let my *controller* setup what it should return, and then check to ensure it returns the right thing.
Bob Yexley
You will need Stubs. The recent version of Moq includes it.
Charles Prakash Dasari
+1  A: 

Hey, I think you're just experiencing a bit of a disconnect here, no big deal. What you describe is 100% possible.

I'm not entirely positive on why you can't set properties on your Mocks, but if you post the full code for your test I'd be happy to go through it with you. Just off the top of my head, I'll suggest two things:

  1. There is a difference between Setup() and SetupProperty(). SetupProperty() is probably what you're after if you want to track values on properties, rather than just get a value from them once.

  2. Alternately try calling SetupAllProperties() on any mock that you need to set a property on.

Check the Moq quickstart as well for some examples.

womp
*That* was very helpful, thank you. I've read through the Moq quickstart, but had either overlooked those methods/examples, or just misunderstood what they were intended to do. Thanks for the clarification. I'm still having some trouble figuring out how to setup my controllers in such a way that I can mock the HttpContext/HttpSession/Request.Cookies objects and be able to get/set them all appropriately, but that's a somewhat different question. Thanks again.
Bob Yexley
Well...I tried this, and it doesn't seem to have actually *done* anything. After I run SetupAllProperties on the mock object, and then view all of the properties on the object (debugging), they're all null. What am I missing or doing wrong?
Bob Yexley
Can you post your actual test?
womp
I thought it would be overkill to post all of my code (Controllers, MembershipProvider, TestFixture, etc), so I created another post/question with a nearly complete sample of what I'm trying to do. I think it should be fairly clear. Thanks for your help.http://stackoverflow.com/questions/1228179/mocking-httpcontextbase-with-moq
Bob Yexley
In case anyone is interested in knowing, I found the solution to what I needed. It was posted on the above linked thread. Thanks again for the assistance.
Bob Yexley
+1  A: 

Not sure if anyone is interested but I have translated the Moq FakeHttpContext to one using Rhino Mocks (my weapon of choice).

public static HttpContextBase FakeHttpContext()
        {
            var httpContext = MockRepository.GenerateMock<HttpContextBase>();
            var request = MockRepository.GenerateMock<HttpRequestBase>();
            var response = MockRepository.GenerateMock<HttpResponseBase>();
            var session = MockRepository.GenerateMock<HttpSessionStateBase>();
            var server = MockRepository.GenerateMock<HttpServerUtilityBase>();
            var cookies = new HttpCookieCollection();
            response.Stub(r => r.Cookies).Return(cookies);
            request.Stub(r => r.Cookies).Return(cookies);
            request.Stub(r => r.Url).Return(new Uri("http://test.com"));

            httpContext.Stub(x => x.Server).Return(server);
            httpContext.Stub(x => x.Session).Return(session);
            httpContext.Stub(x => x.Request).Return(request);
            httpContext.Stub(x => x.Response).Return(response);
            var writer = new StringWriter();
            var wr = new SimpleWorkerRequest("", "", "", "", writer);
            System.Web.HttpContext.Current = new System.Web.HttpContext(wr);
            return httpContext;

        }

with a usage in a Unit Test of

_httpContext = FakeHttpContext();
var cookieManager = new CookieManager(_httpContext);
string username = cookieManager.GetUsername();
_httpContext.AssertWasCalled(hc => { var dummy = hc.Request; });
ArtificialGold