2009-09-15 15 views
12

Próbuję napisać test jednostkowy dla naszej metody wylogowania. Między innymi to FormsAuthentication.SignOut(). Jednak rzuca System.NullReferenceException.Co jest potrzebne w HttpContext, aby umożliwić FormsAuthentication.SignOut() do wykonania?

Stworzyłem próbę; HttpContext (używając Moq), ale oczywiście czegoś brakuje.

Moje mock kontekst zawiera:

  • szydzili HttpRequestBase na Request
  • szydzili HttpResponseBase na Response
  • Przy HttpCookieCollection na Request.Cookies i innym na Response.Cookies
  • szydzili IPrincipal na User

Zdaję sobie sprawę, że mógłbym przejść do otoki i wstrzyknąć pusty obiekt owijający FormsAuth w to miejsce, ale naprawdę chciałbym ominąć 3 dodatkowe pliki, aby naprawić jedną linię kodu. To i wciąż jestem ciekaw na odpowiedź

Więc moje pytanie jest „Co jest potrzebne w HttpContext aby umożliwić FormsAuthentication.SignOut() to execute.

Odpowiedz

8

Oto kod wylogowania.

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); 
    } 
} 

Wygląda jak trzeba instancję CookielessHelperClass. Szkoda, że ​​jest wewnętrzna i zapieczętowana - nie można jej drwić, chyba że używasz TypeMocka. +1 dla sugestii opakowania :)

+0

Doskonała, dokładnie odpowiedź, której szukałem. Pozdrawiam :) – jeef3

+0

To nie jest odpowiedź ... Muszę wyśmiać FormsAuthentication i to mi nie pomoże. –

+1

@Yaroslav Yakovlev - tylko dlatego, że coś ci nie pomoże, nie powinieneś go głosować. To oczywiście pomogło oryginalnemu plakatowi. – womp

13

Zawsze można owinąć FormsAuthentication.SignOut() w inny sposób i odgałęzienie/drwić z tego.

Utwórz interfejs IFormsAuthenticationWrap.

public interface IFormsAuthenticationWrap 
{ 
    void SignOut(); 
} 

Tworzenie klasy oblewania, która implementuje IFormsAuthenticationWrap

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

Twoja klasa wywołujący będzie wyglądać mniej więcej tak:

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 
    } 
} 

Teraz przejdźmy do naszego testu. Możesz odgadnąć/kpić z Moq, ale pokażę ci, jak możesz to zrobić ręcznie. Tworzenie odgałęzienia/klasę makiety:

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

I ostatni napisać test:

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

Możesz wykonać przykład pokazałem ci w zmodyfikowanej odpowiedzi, w jaki sposób możesz złamać swoje zewnętrzne zależności. – Vadim

+0

+1 jak wspomniałem w mojej odpowiedzi, opakowanie jest drogą do zrobienia. – eglasius

2

Owijka jest czysty droga.

Wspomniałeś w komentarzu, że "to będzie całkiem spora aplikacja", to kolejny argument, by użyć opakowania, a nie odwrotnie. W dużej aplikacji chcesz mieć wyraźne zależności i chcesz, aby testy były łatwe.

Handlujesz czystymi zależnościami, które można łatwo wprowadzić w trudnych zależnościach do wewnętrznego działania asp.net w twoich testach.


Na innym uwaga: Korzystanie reflektor. Szczerze mówiąc, nie znam wewnętrznych zależności tej konkretnej części asp.net, ale możesz wyjaśnić wszelkie wątpliwości za pomocą reflektora.

+0

Dzięki za odpowiedź. Usunąłem moje komentarze sugerujące "dużą aplikację", ponieważ od tego czasu zmieniłem moje pytanie. Oczywiście, niezależnie od wielkości aplikacji, tworzenie wrappera i używanie wtrysku zależności dla takich klas/metod jest * sposobem * pójścia. (I będzie tak jak ja, jeśli chcę, żeby ten test jednostkowy przeszedł) Chciałbym jednak nadal pozostawiać moje pytanie takim, jakie jest. Pozdrowienia za wskazówkę na reflektorze, zajrzę w to. – jeef3

1

Nie kpij HttpContext, używaj prawdziwego w testach. W ten sposób nie musisz kpić z tych wszystkich rzeczy Http *. Możesz użyć Ivonna i przetestować swoją metodę bezpośrednio, bez kpienia z tych zależności i uzyskania tajemniczych wyjątków.

18

NullReferenceException w tym przypadku jest faktycznie rzucony przez wywołanie:

Można przetestować ten fakt pod numerem:

HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue 

... który będzie również zwrócić NullReferenceException. W przeciwieństwie do zaakceptowanej odpowiedzi, jeśli spróbujesz zadzwonić pod numer:

CookielessHelperClass.UseCookieless(current, false, CookieMode) 

... z najbliższego okna nastąpi to bezbłędnie.

można naprawić wyjątek takiego:

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

... i wywołanie FormsAuthentication.SignOut() będzie teraz uda.

+3

Świetna odpowiedź, bardzo mi to pomaga. Niestety mam inny wyjątek (ArgumentNullException), ale można go również naprawić, dodając jeszcze jedną funkcję przeglądarki - "cookies". Tak więc ostateczna odpowiedź jest następny: HttpContext.Current.Request.Browser = nowe HttpBrowserCapabilities { Możliwości = new Dictionary { { "supportsEmptyStringInCookieValue", "false"}, { " ciasteczka", " false "}, } }; – msi

Powiązane problemy