Question

I am trying to write a unit test for our log out method. Among other things it FormsAuthentication.SignOut(). However, it throws a System.NullReferenceException.

I've created a mock; HttpContext (using Moq), but it is obviously missing something.

My mock context contains:

  • A mocked HttpRequestBase on Request
  • A mocked HttpResponseBase on Response
  • With a HttpCookieCollection on Request.Cookies and another on Response.Cookies
  • A mocked IPrincipal on User

I am aware I could go the wrapper route and inject an empty FormsAuth wrapper object in it's place, but I would really like to avoid the 3 additional files just to fix one line of code. That and I am still curious for an answer

So my question is "What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute."

Was it helpful?

Solution

Here's the code for signout.

public static void SignOut()
{
    Initialize();
    HttpContext current = HttpContext.Current;
    bool flag = current.CookielessHelper.DoesCookieValueExistInOriginal('F');
    current.CookielessHelper.SetCookieValue('F', null);
    if (!CookielessHelperClass.UseCookieless(current, false, CookieMode) || current.Request.Browser.Cookies)
    {
        string str = string.Empty;
        if (current.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
        {
            str = "NoCookie";
        }
        HttpCookie cookie = new HttpCookie(FormsCookieName, str);
        cookie.HttpOnly = true;
        cookie.Path = _FormsCookiePath;
        cookie.Expires = new DateTime(0x7cf, 10, 12);
        cookie.Secure = _RequireSSL;
        if (_CookieDomain != null)
        {
            cookie.Domain = _CookieDomain;
        }
        current.Response.Cookies.RemoveCookie(FormsCookieName);
        current.Response.Cookies.Add(cookie);
    }
    if (flag)
    {
        current.Response.Redirect(GetLoginPage(null), false);
    }
}

Looks like you need a CookielessHelperClass instance. Too bad it's internal and sealed - there's no way to mock it unless you're using TypeMock. +1 for wrapper suggestions :)

OTHER TIPS

The NullReferenceException in this case is actually being thrown by the call:

current.Request.Browser["supportsEmptyStringInCookieValue"]

You can test this assertion by calling:

HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue

...which will also return the NullReferenceException. Contrary to the accepted answer, if you attempt to call:

CookielessHelperClass.UseCookieless(current, false, CookieMode)

...from the immediate window, this will return without error.

You can fix the exception like this:

HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };

...and the FormsAuthentication.SignOut() call will now succeed.

You can always wrap FormsAuthentication.SignOut() into another method and stub / mock it.

Create IFormsAuthenticationWrap interface.

public interface IFormsAuthenticationWrap
{
    void SignOut();
}

Create wrap class that implements IFormsAuthenticationWrap

public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}

Your calling class is going to look something like this:

public class LogOutClass
{
    private readonly IFormsAuthenticationWrap _formsAuthentication;

    public LogOutClass() : this (new FormsAuthenticationWrap())
    {
    }

    public LogOutClass(IFormsAuthenticationWrap formsAuthentication)
    {
        _formsAuthentication = formsAuthentication;
    }

    public void LogOutMethod()
    {
        // Code before SignOut

        _formsAuthentication.SignOut();

        // Code after SignOut
    }
}

Now let's get to our test. You can stub / mock with Moq but I'm going to show here how you can do it manually. Create your stub / mock class:

public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
    public void SignOut()
    {
    }
}

And the last write the test:

    [TestMethod]
    public void TestLogOutMethod()
    {
        var logOutClass = new LogOutClass(new FormsAuthenticationStub());
        logOutClass.LogOutMethod();
    }

The wrapper is the clean way to go.

You mentioned in a comment that "this is going to be quite a big application", that's another argument to use the wrapper not the opposite. In a big application you want to have clear dependencies, and you want tests to be done easily.

You are trading clean dependencies that can be easily injected over obscure dependencies to the internal workings of asp.net in your tests.


On a different note: Use Reflector. I honestly don't know the inner dependencies of this specific part of asp.net, but you can clear any doubts with reflector.

Don't mock HttpContext, use a real one in your tests. This way you don't have to mock all these Http* stuff. You can use Ivonna and test your method directly, without mocking all these dependencies and getting mysterious exceptions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top