Well, after much banging my head against the wall, I've found a way to test actions that doesn't require you to stop using HttpContext. Do realize that this will not fix your code using the static property System.Web.HttpContext.Current.
Let's see some code
Let's go ahead and create a fresh MVC project in Visual Studio 2015. Make sure to have it create a test project for you, too.Navigate to the HomeController and make the following changes:
public ActionResult About() {
ViewBag.Message = "Your
application description page.";
ViewBag.Url = HttpContext.Request.RawUrl;
return View();
}
Add the following line in the About view:
@{
ViewBag.Title = "About";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>
<p>You navigated to this url: @ViewBag.Url</p>
<p>Use this area to
provide additional information.</p>
Run the project and notice that it displays:
Good, now let's modify the test to make sure that the URL is what we think it should be.
Navigate to the HomeControllerTest file and add the following:
[TestMethod]
public void About() {
// Arrange
HomeController
controller = new HomeController();
// Act
ViewResult result =
controller.About() as ViewResult;
// Assert
Assert.AreEqual("Your application description page.", result.ViewBag.Message);
Assert.AreEqual("http://localhost:59191/Home/About", result.ViewBag.RawUrl);
}
Now run the test and notice: BANG!
It makes sense--you're not in Kansas anymore. And by Kansas, of course, I mean IIS...or whatever baked my fresh HttpContext before. So, HttpContext is null. :'(
MvcContrib Test Helper
Install the following package using the NuGet Package Manager:
- Right-click the References special folder for the test project and click Manage NuGet Packages...
- Search for MvcContrib.TestHelper
- Install
Update the test method and run the About test again
using MvcContrib.TestHelper;
...
[TestMethod]
public void About() {
// Arrange
var controller = new HomeController();
var builder = new TestControllerBuilder();
builder.RawUrl = "http://localhost:59191/Home/About";
builder.InitializeController(controller);
// Act
var result = controller.About() as ViewResult;
// Assert
Assert.AreEqual("Your application description page.", result.ViewBag.Message);
Assert.AreEqual("http://localhost:59191/Home/About", result.ViewBag.Url);
}
Success!!!
That's great, but what if I wanted to use Url instead of RawUrl?
I have to admit, I originally wrote this post with Request.Url in mind. It didn't work--there was no builder.Url hanging around--just RawUrl. But, I did find a way of making that work, too!
Here's the controller code:
public ActionResult About() {
ViewBag.Message = "Your
application description page.";
ViewBag.RawUrl = HttpContext.Request.RawUrl;
ViewBag.Url = HttpContext.Request.Url;
return View();
}
Here's the Test code:
using Rhino.Mocks; //This gets installed with MvcContrib Test Helper
...
[TestMethod]
public void About() {
// Arrange
var controller = new HomeController();
var builder = new TestControllerBuilder();
builder.RawUrl = "http://localhost:59191/Home/About";
builder.InitializeController(controller);
builder.HttpContext.Request.Stub(x => x.Url).Return(new Uri("http://localhost:59191/Home/About"));
// Act
var result = controller.About() as ViewResult;
// Assert
Assert.AreEqual("Your application description page.", result.ViewBag.Message);
Assert.AreEqual("http://localhost:59191/Home/About", result.ViewBag.RawUrl);
Assert.AreEqual("http://localhost:59191/Home/About", result.ViewBag.Url.ToString());
}
Run the test:
BAM! SCORE!
It gets better!
Here's a list of properties hanging off of TestControllerBuilder just begging to be used:
- Files
- Form
- HttpContext
- PathInfo
- QueryString
- RawUrl
- RouteData
- Session
- TempDataDictionary
Here are a few good references on how to use the other features of MvcContrib TestHelper:
- MvcContrib Test Helper documentation
- Adventures with the MvcContrib TestHelper
- A Sample MVC Project (Controller Tests)
Summary
- MVC is testable
- We all like to use HttpContext
- HttpContext is a pain to set up and inject for testing controller actions
- If you do nothing, HttpContext, Request, Response, etc. will all be null
- Idea: Use MvcContrib Test Helper
- Use its easy-access properties where you can
- Use RhinoMocks to fill in the holes
Do you have something awesome to add to this post? Great! Leave a comment.
Do you know of a better way? Great! Leave a comment.
Did I do something that you hate? Great! Leave a comment.
The more we engage, the better we'll all be at our jobs.
Thank you for reading, sharing, and engaging--and God bless!
Brandon
No comments:
Post a Comment