Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP/NET MVC: Test Controllers w/Sessions? Mocking?

I read some of the answers on here re: testing views and controllers, and mocking, but I still can't figure out how to test an ASP.NET MVC controller that reads and sets Session values (or any other context based variables.) How do I provide a (Session) context for my test methods? Is mocking the answer? Anybody have examples? Basically, I'd like to fake a session before I call the controller method and have the controller use that session. Any ideas?

like image 982
Codewerks Avatar asked Oct 06 '08 21:10

Codewerks


3 Answers

Check out Stephen Walther's post on Faking the Controller Context:

ASP.NET MVC Tip #12 – Faking the Controller Context

[TestMethod]
public void TestSessionState()
{
    // Create controller
    var controller = new HomeController();


    // Create fake Controller Context
    var sessionItems = new SessionStateItemCollection();
    sessionItems["item1"] = "wow!";
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems);
    var result = controller.TestSession() as ViewResult;


    // Assert
    Assert.AreEqual("wow!", result.ViewData["item1"]);

    // Assert
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]);
}
like image 140
David P Avatar answered Oct 13 '22 03:10

David P


The ASP.NET MVC framework is not very mock-friendly (or rather, requires too much setup to mock properly, and causes too much friction when testing, IMHO) due to it's use of abstract base classes instead of interfaces. We've had good luck writing abstractions for per-request and session-based storage. We keep those abstractions very light and then our controllers depend upon those abstractions for per-request or per-session storage.

For example, here's how we manage the forms auth stuff. We have an ISecurityContext:

public interface ISecurityContext
{
    bool IsAuthenticated { get; }
    IIdentity CurrentIdentity { get; }
    IPrincipal CurrentUser { get; set; }
}

With a concrete implementation like:

public class SecurityContext : ISecurityContext
{
    private readonly HttpContext _context;

    public SecurityContext()
    {
        _context = HttpContext.Current;
    }

    public bool IsAuthenticated
    {
        get { return _context.Request.IsAuthenticated; }
    }

    public IIdentity CurrentIdentity
    {
        get { return _context.User.Identity; }
    }

    public IPrincipal CurrentUser
    {
        get { return _context.User; }
        set { _context.User = value; }
    }
}
like image 14
chadmyers Avatar answered Oct 13 '22 03:10

chadmyers


With MVC RC 1 the ControllerContext wraps the HttpContext and exposes it as a property. This makes mocking much easier. To mock a session variable with Moq do the following:

var controller = new HomeController();
var context = MockRepository.GenerateStub<ControllerContext>();
context.Expect(x => x.HttpContext.Session["MyKey"]).Return("MyValue");
controller.ControllerContext = context;

See Scott Gu's post for more details.

like image 10
Dane O'Connor Avatar answered Oct 13 '22 01:10

Dane O'Connor