2010-08-04 7 views
1

J'ai un problème avec la méthode RouteUrl d'UrlHelper qui ne renvoie qu'une chaîne vide lors de mes tests, mais fonctionne correctement lors de l'exécution dans le vrai HttpContext. C'est, cependant, trouver la route - comme je le fais correctement obtenir une exception si j'essaie de résoudre un nom de route qui n'a pas été défini.UrlHelper's RouteUrl retournant une chaîne vide dans les tests

Je moqué HttpContext et amis à l'aide de la code provided by Scott Hanselman/Kzu et a ajouté le code needed to bootstrap the Application's Routes dans l'instance moqué

Pour réduire le nombre de variables dans ma situation, j'ai écrit un test simple:

[Test] 
public void UrlHelperReturnsCorrectUrl() 
{ 
    var controller = new MyController(); 
    controller.SetFakeControllerContext().LoadUrlHelper(); 

    Assert.AreEqual("My/Route/Path", controller.Url.RouteUrl("MyRoute")); 
} 

Il est intéressant, l'accès au RouteCollection directement et en utilisant VirtualPath fonctionne le:

[Test] 
public void GetVirtualPathReturnsCorrectUrl() 
{ 
    var controller = new AccountController(); 
    controller.SetFakeControllerContext().LoadUrlHelper(); 
    Assert.AreEqual("My/Route/Path", 
       Controller.Url.RouteCollection["MyRoute"] 
       .GetVirtualPath(
        controller.Url.RequestContext, 
        new RouteValueDictionary()) 
       .VirtualPath); 
} 

Pour consulter ence, Voici ma mise en œuvre de la méthode d'extension LoadUrlHelper:

public static Controller LoadUrlHelper(this Controller controller) 
{ 
    var routes = new RouteCollection(); 
    MvcApplication.RegisterRoutes(routes); 
    controller.Url = new UrlHelper(
         controller.ControllerContext.RequestContext, 
         routes); 
    return controller; 
} 

Et voici mon itinéraire tel que défini dans Global.asax de ma demande:

routes.MapRoute(
    "MyRoute", "My/Route/Path", 
    new {controller = "Home", action = "Index"}); 

Quelqu'un at-il rencontré ce? Est-ce que je manque quelque chose?

EDIT:

J'ai suivi le code MVC jusqu'au point qu'il tend le traitement des acheminements hors de System.Routing et a trouvé quelque chose de très intéressant. Le code MVC court terme à rechercher l'URL désirée (condensé, bien sûr) retourne une chaîne vide:

Controller.Url.RouteCollection.GetVirtualPath(
      Controller.Url.RequestContext, 
      "MyRoute", new RouteValueDictionary()).VirtualPath; 

alors une variante très similaire retourne la chaîne attendue:

Controller.Url.RouteCollection["MyRoute"].GetVirtualPath(
      Controller.Url.RequestContext, 
      new RouteValueDictionary()).VirtualPath; 

Je ne peux pas semble aller plus loin dans le code sous-jacent pour voir ce qui se passe réellement différemment ici, mais pensait que cela pourrait aider quelqu'un à comprendre quelle configuration me manque. (Je ne vais pas encore crier de bogue, car le fait est que les UrlHelpers fonctionnent quand ils sont dans un vrai HttpContext)

+0

Qu'est-ce que vous essayez de tester ici? Il semble que ce que fait votre test vérifie que la classe UrlHelper des framework MVC communique correctement avec le système de routage. À mon avis, vous ne devriez pas avoir à tester pour cela parce que l'équipe MVC a déjà testé pour cela. – marcind

+0

J'essayais de simplifier l'installation pour la présentation seulement, de sorte que je n'ai pas eu à montrer aussi le contrôleur/code d'action pour montrer le problème que je éprouve. Ma configuration "réelle" a l'appel Url.RouteUrl à l'intérieur de mon action de contrôleur et mon test vérifie la valeur dans ViewData.Model de ViewResult. Le code que j'ai présenté aboutit au même résultat, cependant. –

Répondre

1

La solution à mon problème était déjà postée sur another SO question.

J'avais essayé d'incorporer cette solution à la pièce plus tôt mais je l'ai fait si mal. Une fois que je l'ai copié entièrement et modifié pour ma situation, cela a fonctionné parfaitement.

Voici une version plus générique qui peut être réutilisée dans de nombreux tests (si elle est placée dans une classe d'appareils de test de base ou quelque chose de similaire).

Utilisation:

var controller = GetController<MyController>(); 
controller.MyAction(); 
//... 

Méthode:

protected T GetController<T>() where T : Controller, new() 
{ 
    var routes = new RouteCollection(); 
    MvcApplication.RegisterRoutes(routes); 

    var request = new Mock<HttpRequestBase>(MockBehavior.Strict); 
    request.SetupGet(x => x.ApplicationPath).Returns("/"); 
    request.SetupGet(x => x.Url).Returns(new Uri("http://localhost", UriKind.Absolute)); 
    request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection()); 

    var response = new Mock<HttpResponseBase>(MockBehavior.Strict); 
    response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string p) => p); 

    var context = new Mock<HttpContextBase>(MockBehavior.Strict); 
    context.SetupGet(x => x.Request).Returns(request.Object); 
    context.SetupGet(x => x.Response).Returns(response.Object); 

    var controller = new T(); 
    controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller); 
    controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes); 

    return controller; 
} 
Questions connexes