[c#] Moq: Unit testet eine Methode, die auf HttpContext basiert



2 Answers

Im Allgemeinen sollten Sie für das Testen von ASP.NET-Einheiten anstelle von HttpContext.Current eine Eigenschaft vom Typ HttpContextBase verwenden, deren Wert durch die Abhängigkeitsinjektion festgelegt wird (z. B. in der Antwort von Womp).

Zum Testen sicherheitsrelevanter Funktionen würde ich jedoch Thread.CurrentThread.Principal (anstelle von HttpContext.Current.User) empfehlen. Die Verwendung von Thread.CurrentThread hat den Vorteil, dass es auch außerhalb eines Webkontexts wiederverwendbar ist (und in einem Webkontext funktioniert, da das ASP.NET-Framework immer beide Werte gleich setzt).

Um dann Thread.CurrentThread.Principal zu testen, verwende ich normalerweise eine Scope-Klasse, die den Thread.CurrentThread auf einen Testwert setzt und dann auf dispose zurücksetzt:

using (new UserResetScope("LOONEYTUNES\BUGSBUNNY")) {
    // Put test here -- CurrentThread.Principal is reset when PrincipalScope is disposed
}

Dies passt gut zur Standard-.NET-Sicherheitskomponente, bei der eine Komponente über eine bekannte Schnittstelle (IPrincipal) und einen Speicherort (Thread.CurrentThread.Principal) verfügt und mit jedem Code arbeitet, der Thread.CurrentThread.Principal korrekt verwendet / prüft .

Eine Basis-Scope-Klasse würde etwa so aussehen (passen Sie sie zB für das Hinzufügen von Rollen an):

class UserResetScope : IDisposable {
    private IPrincipal originalUser;
    public UserResetScope(string newUserName) {
        originalUser = Thread.CurrentPrincipal;
        var newUser = new GenericPrincipal(new GenericIdentity(newUserName), new string[0]);
        Thread.CurrentPrincipal = newUser;
    }
    public IPrincipal OriginalUser { get { return this.originalUser; } }
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing) {
        if (disposing) {
            Thread.CurrentPrincipal = originalUser;
        }
    }
}

Eine weitere Alternative besteht darin, dass Sie anstelle der standardmäßigen Sicherheitskomponentenposition Ihre App für die Verwendung injizierter Sicherheitsdetails schreiben, z. B. eine ISecurityContext-Eigenschaft mit einer GetCurrentUser () -Methode oder einer ähnlichen Methode hinzufügen und diese dann konsistent in Ihrer gesamten Anwendung verwenden Wenn Sie dies im Kontext einer Webanwendung tun, können Sie auch den vordefinierten, injizierten Kontext, HttpContextBase, verwenden.

Question

Betrachten Sie eine Methode in einer .NET-Assembly:

public static string GetSecurityContextUserName()
{             
 //extract the username from request              
 string sUser = HttpContext.Current.User.Identity.Name;
 //everything after the domain     
 sUser = sUser.Substring(sUser.IndexOf("\\") + 1).ToLower();

 return sUser;      
}

Ich möchte diese Methode aus einem Komponententest mit dem Moq-Framework aufrufen. Diese Baugruppe ist Teil einer Webforms-Lösung. Der Komponententest sieht so aus, aber mir fehlt der Moq-Code.

//arrange 
 string ADAccount = "BUGSBUNNY";
 string fullADName = "LOONEYTUNES\BUGSBUNNY"; 

 //act    
 //need to mock up the HttpContext here somehow -- using Moq.
 string foundUserName = MyIdentityBL.GetSecurityContextUserName();

 //assert
 Assert.AreEqual(foundUserName, ADAccount, true, "Should have been the same User Identity.");

Frage :

  • Wie kann ich Moq verwenden, um ein gefälschtes HttpContext-Objekt mit einem Wert wie 'MyDomain \ MyUser' anzuordnen?
  • Wie verknüpfe ich diese Fälschung mit meinem Aufruf in meine statische Methode bei MyIdentityBL.GetSecurityContextUserName() ?
  • Haben Sie Vorschläge, wie Sie diesen Code / diese Architektur verbessern können?



Dies hängt nicht wirklich damit zusammen, ob Sie Moq zum Komponententest verwenden, was Sie benötigen.

Im Allgemeinen haben wir eine mehrschichtige Architektur, bei der der Code auf der Präsentationsebene nur dazu dient, Dinge so anzuordnen, dass sie auf der Benutzeroberfläche angezeigt werden. Diese Art von Code wird nicht durch Komponententests abgedeckt. Der gesamte Rest der Logik befindet sich auf der Business-Schicht, die keine Abhängigkeit von der Darstellungsschicht haben muss (dh UI-spezifische Referenzen wie der HttpContext), da die UI auch eine WinForms-Anwendung und nicht unbedingt eine Webanwendung sein kann .

Auf diese Weise können Sie vermeiden, sich mit Mock-Frameworks herumzuschlagen, HttpRequests usw. zu simulieren, obwohl dies oft noch notwendig ist.




[TestInitialize]
public void TestInit()
{
  HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
}

Auch können Sie moq wie unten

var controllerContext = new Mock<ControllerContext>();
      controllerContext.SetupGet(p => p.HttpContext.Session["User"]).Returns(TestGetUser);
      controllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://web1.ml.loc"));



Related