2010-06-18 5 views
5

J'ai des difficultés à tester les contrôleurs. mon contrôleur d'origine pour le test a regardé quelque chose comme ceci:asp.net mvc Comment tester les contrôleurs correctement

SomethingController CreateSomethingController() 
{ 
    var somethingData = FakeSomethingData.CreateFakeData(); 
    var fakeRepository = FakeRepository.Create(); 

    var controller = new SomethingController(fakeRepository); 

    return controller; 
} 

Cela fonctionne bien pour la majorité des tests jusqu'à ce que je suis la partie Request.IsAjaxRequest() du code. Alors j'ai dû simuler le HttpContext et HttpRequestBase. Donc, mon code a alors changé pour ressembler à:

public class FakeHttpContext : HttpContextBase 
{ 
    bool _isAjaxRequest; 

    public FakeHttpContext(bool isAjaxRequest = false) 
    { 
     _isAjaxRequest = isAjaxRequest; 
    } 

    public override HttpRequestBase Request 
    { 
     get 
     { 
      string ajaxRequestHeader = ""; 

      if (_isAjaxRequest) 
       ajaxRequestHeader = "XMLHttpRequest"; 

      var request = new Mock<HttpRequestBase>(); 
      request.SetupGet(x => x.Headers).Returns(new WebHeaderCollection 
      { 
       {"X-Requested-With", ajaxRequestHeader} 
      }); 

      request.SetupGet(x => x["X-Requested-With"]).Returns(ajaxRequestHeader); 

      return request.Object; 
     } 
    } 

    private IPrincipal _user; 

    public override IPrincipal User 
    { 
     get 
     { 
      if (_user == null) 
      { 
       _user = new FakePrincipal(); 
      } 
      return _user; 
     } 
     set 
     { 
      _user = value; 
     } 
    } 
} 


SomethingController CreateSomethingController() 
{ 
    var somethingData = FakeSomethingData.CreateFakeData(); 
    var fakeRepository = FakeRepository.Create(); 

    var controller = new SomethingController(fakeRepository); 

    ControllerContext controllerContext = new ControllerContext(new FakeHttpContext(isAjaxRequest), new RouteData(), controller); 
    controller.ControllerContext = controllerContext; 

    return controller; 
} 

Maintenant son arrivé à ce stade de mon contrôleur où j'appelle Url.Route et Url est nulle. Donc, il semble que je dois commencer à se moquer des routes pour mon contrôleur.

Je semble passer plus de temps à googler sur la façon de simuler/simuler des objets et ensuite déboguer pour s'assurer que mes contrefaçons sont correctes que l'écriture réelle du code de test. Existe-t-il un moyen plus facile de tester un contrôleur? J'ai regardé le TestControllerBuilder de MvcContrib qui aide avec certains des problèmes mais ne semble pas faire tout. Y at-il autre chose disponible qui fera l'affaire et me permettra de me concentrer sur l'écriture des tests plutôt que d'écrire des simulacres?

Merci

+1

Le problème n'est pas avec vos tests, c'est avec votre action de contrôleur. Vous devriez essayer d'éviter d'utiliser le contexte http dans votre action et n'utiliser que les dépendances que vous fournissez dans votre constructeur. Les actions doivent être très petites. –

Répondre

1

Vous pouvez utiliser quelques-unes des bibliothèques qui vous donnent de la boîte certains de ces objets. Par exemple RhinoMock, NMock ... etc. J'utilise personnellement Moq - c'est assez bon et gratuit. Ce que j'aime le plus dans Moq, ce sont les expressions linq.

1

La plupart des moteurs moqueurs feront tout cela pour vous. J'utilise RhinoMocks mais il y a beaucoup plus disponible. Aussi Moles est moteur de moquerie très nouveau et intéressant (cela vient généralement avec Pex qui est encore plus de munitions dans votre arsenal de test de l'unité)

1

MvcContrib + RhinoMocks. Consultez le TestControllerBuilder dans la bibliothèque MvcContrib.TestHelper. Voici l'article officiel: http://mvccontrib.codeplex.com/wikipage?title=TestHelper#Examples.

Voici un exemple de se moquant un contrôleur pour tester un UrlHelper: ASP.NET MVC: Mock controller.Url.Action

Voici une brève explication de l'utilisation de la TestControllerBuilder: http://codebetter.com/blogs/kyle.baley/archive/2008/03/19/testcontrollerbuilder-in-mvccontrib.aspx

1

Au lieu de se moquer des choses, vous pouvez passer IAjaxRequest au constructeur. Ou faites-en la propriété de classe constructeur de base (et utilisez l'injection de propriété). Ou vous pouvez demander à votre constructeur d'implémenter IAjaxRequest, puis d'appliquer un filtre d'action globale sur la classe de constructeur de base qui va configurer IAjaxRequest.

Cela aidera à faire abstraction de beaucoup de choses, y compris des trucs HttpContext. Ne résumez pas simplement IHttpContext, résumé IUserContext, ISessionStorage, IAuthentication, IRequestDetails ...

Une autre façon consiste à utiliser directement le modèle de classeur sur des méthodes où vous avez besoin d'informations spécifiques. Voir this post par exemple. Vous pouvez faire un classeur qui vous donnera IsAjaxRequest, alors vous venez de faire une action pour accepter ce paramètre. Fonctionne très bien parce que l'information est fournie exactement à la méthode qui en a besoin, pas à l'ensemble du contrôleur.

Questions connexes