15 Stimmen

ASP.NET MVC Controller Unit Testing - Problem mit UrlHelper-Erweiterung

Ich versuche, etwas zu tun Controller Unit-Testing in meiner ASP.NET MVC 3-Webanwendung.

Mein Test läuft folgendermaßen ab:

[TestMethod]
public void Ensure_CreateReviewHttpPostAction_RedirectsAppropriately()
{
   // Arrange.
   var newReview = CreateMockReview();

   // Act.
   var result = _controller.Create(newReview) as RedirectResult;

   // Assert.
   Assert.IsNotNull(result, "RedirectResult was not returned");
}

Ziemlich einfach. Im Grunde testen Sie eine [HttpPost] Aktion, um sicherzustellen, dass sie eine RedirectResult (PRG-Muster). Ich verwende nicht RedirectToRouteResult weil keine der Überladungen Ankerlinks unterstützt. Weiter geht's.

Jetzt benutze ich Moq um den Http-Kontext nachzubilden, einschließlich Servervariablen, Controller-Kontext, Sitzung usw. Alles läuft gut, so weit.

Bis ich diese Zeile in meiner Aktionsmethode erreicht habe:

return Redirect(Url.LandingPageWithAnchor(someObject.Uri, review.Uri);

LandingPageWithAnchor ist eine benutzerdefinierte HTML-Hilfsfunktion:

public static string LandingPageWithAnchor(this UrlHelper helper, string uri1, string uri2)
{
   const string urlFormat = "{0}#{1}";

   return string.Format(urlFormat,
                helper.RouteUrl("Landing_Page", new { uri = uri1}),
                uri2);
}

Im Grunde leite ich auf eine andere Seite weiter, die eine "Landing Page" für neue Inhalte ist, mit einem Anker auf die neue Rezension. Cool.

Diese Methode schlug zuvor fehl, weil UrlHelper war null.

Also habe ich dies in meinem Spott getan:

controller.Url = new UrlHelper(fakeRequestContext);

Das hat es weitergebracht, aber jetzt schlägt es fehl, weil die Routentabellen keine Definition für "Landing_Page" enthalten.

Ich weiß also, dass ich mich über "etwas" lustig machen muss, aber ich bin nicht sicher, ob es das ist:

a) Die Streckentabellen
b) Die Methode UrlHelper.RouteUrl
c) Die von mir geschriebene Erweiterungsmethode UrlHelper.LandingPageWithAnchor

Kann jemand einen Rat geben?

EDIT

Diese besondere Route befindet sich in einem Bereich also habe ich versucht, die Bereichsregistrierung in meinem Unit-Test aufzurufen:

AreaRegistration.RegisterAllAreas();

Aber ich bekomme eine InvalidOperationException :

Diese Methode kann nicht während der Initialisierungsphase vor dem Start der Anwendung aufgerufen werden.

12voto

RPM1984 Punkte 70972

Haben Sie es arbeiten durch Mocking der HttpContext, RequestContext und ControllerContext, die Registrierung der Routen dann erstellen eine UrlHelper mit diesen Routen.

Das geht in etwa so:

public static void SetFakeControllerContext(this Controller controller, HttpContextBase httpContextBase)
{
    var httpContext = httpContextBase ?? FakeHttpContext().Object;
    var requestContext = new RequestContext(httpContext, new RouteData());
    var controllerContext = new ControllerContext(requestContext, controller);
    MvcApplication.RegisterRoutes();
    controller.ControllerContext = controllerContext;
    controller.Url = new UrlHelper(requestContext, RouteTable.Routes);
}

FakeHttpContext() ist ein Moq-Helfer, der alle Mocks, Servervariablen, Sitzungen usw. erstellt.

0voto

ataddeini Punkte 4901

Es gibt eine Konstrukteur para UrlHelper die eine RouteCollection als zweites Argument. Wenn Sie die Standardeinstellung haben, die MVC für Sie erstellt, dann denke ich, dass dies für Sie funktionieren sollte:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

controller.Url = new UrlHelper(fakeRequestContext, routes);

Eine Alternative besteht darin, das Startverhalten Ihrer Anwendung zu ändern, um das Testen und die Arbeit damit zu erleichtern. Wenn Sie eine Schnittstelle wie diese definieren:

public interface IMvcApplication
{
    void RegisterRoutes(RouteCollection routes);
    // Other startup operations    
}

Mit einer Implementierung:

public class MyCustomApplication : IMvcApplication
{
    public void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        // Your route registrations here
    }

    // Other startup operations
}

Dann können Sie Ihre Global.asax wie diese:

public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        var app = new MyCustomApplication();
        app.RegisterRoutes(RouteTable.Routes);
        // Other startup calls
    }
}

Und Sie haben immer noch die Möglichkeit, Ihre Routen für Tests zu registrieren. Etwas wie dies:

private IMvcApplication _app;
private RouteCollection _routes;

[TestInitialize]
public void InitializeTests()
{
    _app = new MyCustomApplication();

    _routes = new RouteCollection();
    _app.RegisterRoutes(_routes);
}

Dies kann in ähnlicher Weise auch für die Arbeit mit Gebietsregistrierungen genutzt werden.

private RouteCollection _routes;
private MyCustomAreaRegistration _area;

[TestInitialize]
public void InitTests()
{
    _routes = new RouteCollection();
    var context = new AreaRegistrationContext("MyCustomArea", _routes);

    _area.RegisterArea(context);
}

0voto

DavidAndroidDev Punkte 2361

Da es sich hier um einen Test handelt, liest Ihre Test-Suite die Global.asax höchstwahrscheinlich nicht so, wie Sie es vielleicht erwarten.

Um dies zu umgehen, tun Sie, was @ataddeini vorgeschlagen hat, und erstellen Sie eine Routensammlung. Fügen Sie zu dieser Sammlung eine neue Route hinzu, die wie folgt aussieht...

var routes = new RouteCollection();
routes.Add(new Route("Landing_Page", new { /*pass route params*/}, null));
var helper = new UrlHelper(fakeRequestContext, routes);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X