c# callback - Moq:Unit testet eine Methode, die auf HttpContext basiert



verify setupproperty (6)

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?

Answers

[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"));

Webforms ist aus genau diesem Grund notorisch nicht testbar - viel Code kann sich auf statische Klassen in der asp.net-Pipeline verlassen.

Um dies mit Moq zu testen, müssen Sie die Methode GetSecurityContextUserName() , um die Abhängigkeitsinjektion mit einem HttpContextBase Objekt zu verwenden.

HttpContextWrapper befindet sich in System.Web.Abstractions , das mit .Net 3.5 ausgeliefert wird. Es ist ein Wrapper für die HttpContext Klasse und erweitert HttpContextBase , und Sie können einen HttpContextWrapper wie HttpContextWrapper :

var wrapper = new HttpContextWrapper(HttpContext.Current);

Noch besser, Sie können eine HttpContextBase überspielen und Ihre Erwartungen mit Moq festlegen. Einschließlich des eingeloggten Benutzers usw.

var mockContext = new Mock<HttpContextBase>();

GetSecurityContextUserName(mockContext.Object) können Sie GetSecurityContextUserName(mockContext.Object) , und Ihre Anwendung ist viel weniger mit dem statischen WebForms-HttpContext gekoppelt. Wenn Sie viele Tests durchführen, die auf einem gespielten Kontext beruhen, schlage ich vor , einen Blick auf Scott Hanselmans MvcMockHelpers-Klasse zu werfen , die eine Version zur Verwendung mit Moq hat. Es behandelt bequem eine Menge des notwendigen Setups. Und trotz des Namens, müssen Sie es nicht mit MVC tun - ich benutze es erfolgreich mit Webforms-Apps, wenn ich sie HttpContextBase kann, um HttpContextBase zu verwenden.


Werfen Sie einen Blick auf diese http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx

Mit der httpSimulator-Klasse können Sie einen HttpContext an den Handler übergeben

HttpSimulator sim = new HttpSimulator("/", @"C:\intepub\?")
.SimulateRequest(new Uri("http://localhost:54331/FileHandler.ashx?
ticket=" + myticket + "&fileName=" + path));

FileHandler fh = new FileHandler();
fh.ProcessRequest(HttpContext.Current);

HttpSimulator implementiert, was wir benötigen, um eine HttpContext-Instanz zu bekommen. Sie müssen Moq hier also nicht verwenden.


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.


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.


API Sanity Checker - Test Framework für C / C ++ Bibliotheken:

Ein automatischer Generator von Basiseinheitstests für eine gemeinsame C / C ++ Bibliothek. Es ist in der Lage, (in den meisten, aber leider nicht allen Fällen) Eingabedaten für Parameter zu erzeugen und einfache ("Vernunft" oder "flache") Testfälle für jede Funktion in der API durch die Analyse von Deklarationen im Header zu erstellen Dateien.

Die Qualität der generierten Tests ermöglicht die Überprüfung der Abwesenheit von kritischen Fehlern in einfachen Anwendungsfällen. Das Tool ist in der Lage, generierte Tests zu erstellen und auszuführen und Abstürze (segfaults), Abbrüche, alle Arten von ausgesendeten Signalen, Programm-Rückkehrcode ungleich Null und Programmbehang zu erkennen.

Beispiele:





c# unit-testing mocking moq