title: How to mock HttpContext in a ShoppingCart controller/model
body: <p>In our MVC4 application with Entity Framework 4.0 based on the <a href="http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-8" rel="nofollow">Music Store Tutorial</a> we are using Moq to mock the <code>DbContext</code> and unit test are logic. One of our methods proves difficult to test though since it makes use of <code>HttpContext</code> or <code>HttpContextBase</code>. One example method looks like this:</p>
  
  <pre><code>public static ShoppingCart GetCart(HttpContextBase context)
      {
          var cart = new ShoppingCart();
          cart.ShoppingCartId = cart.GetCartId(context);
          return cart;
      }
  </code></pre>
  
  <p>The only property collected from <code>HttpContextBase</code> is the <code>[CartSessionKey]</code> as can be seen here:</p>
  
  <pre><code>public string GetCartId(HttpContextBase context)
  {
      if (context.Session[CartSessionKey] == null)
      {
          if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
          {
              context.Session[CartSessionKey] =
                  context.User.Identity.Name;
          }
          else
          {
              // Generate a new random GUID using System.Guid class
              Guid tempCartId = Guid.NewGuid();
              // Send tempCartId back to client as a cookie
              context.Session[CartSessionKey] = tempCartId.ToString();
          }
      }
      return context.Session[CartSessionKey].ToString();
  }
  </code></pre>
  
  <p>We have heard horror stories that <code>HttpContext</code> is a very complex class and that if you print it you have enough paper to circle the earth eight times.</p>
  
  <p>Nevertheless we want to mock it. The question is how. The properties that we want to mock are the <code>[CartSessionKey]</code>, and the property that come from the context as <code>contest.User.Identity.Name</code>.</p>
  
  <p>We suspect we need to use something like this:</p>
  
  <pre><code>        var mockData = new Mock&lt;FakeContext&gt;();
          mockData.Setup(m =&gt; m.Orders).Returns(memoryOrderItems);
          mockData.Setup(m =&gt; m.Carts).Returns(memoryCartItems);
  
          Mock&lt;HttpContextBase&gt; mockHttpContext = new Mock&lt;HttpContextBase&gt;();
          Mock&lt;HttpRequestBase&gt; mockHttpRequest = new Mock&lt;HttpRequestBase&gt;();
  
          mockHttpRequest.Setup(x =&gt; x.CartSessionKey).Returns(1);
          mockHttpContext.Setup(x =&gt; x.Request).Returns(mockHttpRequest.Object);
  </code></pre>
  
  <p>but we cannot find how to specifically implement this so we do not get any errors on methods that use <code>context.Session[CartSessionKey]</code> or <code>context.User.Identity.Name</code>.</p>
  
  <p>We hope someone can help us out.</p>
  
  <p>/edit</p>
  
  <p>When we do this:</p>
  
  <pre><code> var memoryUserItems = new FakeDbSet&lt;User&gt;()
          {
              new User { Email = "test@test.de",
                         FullName = "Test Person",
                         isAvailable = true,
                         Name = "WHat"
              },
              new User { Email = "test2@test.de",
                         FullName = "Test Person 2",
                         isAvailable = true,
                         Name = "WHat 2"
              }
          };
   (...) Other memory...Items
  </code></pre>
  
  <p>And then this:</p>
  
  <pre><code>        // Create mock units of work
          var mockData = new Mock&lt;FakeContext&gt;();
          mockData.Setup(m =&gt; m.Orders).Returns(memoryOrderItems);
          mockData.Setup(m =&gt; m.Carts).Returns(memoryCartItems);
          mockData.Setup(m =&gt; m.Users).Returns(memoryUserItems);
  
          var principalMock = new Mock&lt;IPrincipal&gt;();
          var identityMock = new Mock&lt;IIdentity&gt;();
          var userMock = 
          identityMock.Setup(x =&gt; x.Name).Returns("Test!");
          identityMock.Setup(x =&gt; x.IsAuthenticated).Returns(true); // optional ;)
          mockData.Setup(x =&gt; x.Identity).Returns(identityMock.Object);
          var httpReqBase = new Mock&lt;HttpRequestBase&gt;(); // this is useful if you want to test Ajax request checks or cookies in the controller.
          var httpContextBase = new Mock&lt;HttpContextBase&gt;();
  
          httpContextBase.Setup(x =&gt; x.User).Returns(principalMock.Object);
          httpContextBase.Setup(x =&gt; x.Session[It.IsAny&lt;string&gt;()]).Returns(1); //Here is the session indexer. You can swap 'any' string for specific string.
          httpContextBase.Setup(x =&gt; x.Request).Returns(httpReqBase.Object);
  </code></pre>
  
  <p>We get the error that:</p>
  
  <blockquote>
    <p>Error 3   'project.Models.FakeContext' does
    not contain a definition for 'Identity' and no extension method
    'Identity' accepting a first argument of type
    'project.Models.FakeContext' could be found
    (are you missing a using directive or an assembly
    reference?)</p>
  </blockquote>
  
  <p>/ edit2</p>
  
  <p>To make it more clear. The actual method I am testing is the following: </p>
  
  <pre><code>    public ActionResult Complete(int id)
      {
          // Make sure that user is currentuser and otherwise bring user to our Thief page
          if (id != db.GetCurrentUserId())
          {
              return View("Thief");
          }
  
          var cart = ShoppingCart.GetCart(this.HttpContext);
          var currentDate = DateTime.Today;
          var viewModel = new ShoppingCartViewModel
          {
              CartItems = cart.GetCartItems(),
              CartTotal = cart.GetTotal(),
              ProductItems = db.Products.ToList()
          };
  
          if (viewModel.CartItems.Count() == 0)
          {
              return View("Empty");
          }
  
          // Try to write cart to order table
          try
          {
              foreach (var item in viewModel.CartItems)
              {
                  ProcessOrder(item, id, currentDate);
  
              }
              // after this we empty the shopping cart
              cart.EmptyCart();
              return View();
          }
          catch
          {
              // Invalid - display error page
              return View("Error");
          }
  
      }
  </code></pre>
  
  <p>As can be seen the <code>var cart = ShoppingCart.GetCart(this.HttpContext);</code> uses <code>this.HttpContext</code>. In the test I just do <code>controller.Complete(1)</code>. I cannot pass a new <code>HttpContext</code> to the controller I guess?</p>
  
  <p>/ edit 3</p>
  
  <p>While using the code below with the mocks I get the following message:</p>
  
  <pre><code>Test Name:  TestCheckoutCompleteShouldWithEmptyCart
  Test FullName:  Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart
  Test Source:    Controllers\CheckoutControllerTest.cs : line 141
  Test Outcome:   Failed
  Test Duration:  0:00:00.0158591
  
  Result Message: 
  Test method Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart threw exception: 
  System.NullReferenceException: Object reference not set to an instance of an object.
  Result StackTrace:  
  at Models\ShoppingCart.cs:line 170
     at \Models\ShoppingCart.cs:line 20
     at \Controllers\CheckoutController.cs:line 48
     at Controllers\CheckoutControllerTest.cs:line 143
  </code></pre>