2010-08-06 12 views
0

J'essaie de tester un résultat d'action personnalisé. J'ai récemment regardé l'excellente vidéo MvcConf de Jimmy Bogard ("Mettez vos contrôleurs sur un régime") http://www.viddler.com/explore/mvcconf/videos/1/ et ont commencé à essayer et à mettre en œuvre des résultats d'action personnalisés. J'ai réussi cela sans problème, l'ActionResult fonctionne bien à l'exécution, mais j'ai de la difficulté à essayer de les tester.Comment tester un test d'action personnalisé

Malheureusement dans le téléchargement de code, il n'y a pas de tests unitaires pour les méthodes d'action personnalisé Jimmy ... qui me font merveille.

Je réalise que les méthodes d'action renvoient juste des instances des types ActionResult et c'est la structure MVC qui appelle réellement la méthode ExecuteResult, qui bien sûr n'est pas disponible lors de l'exécution du test unitaire. Donc, mon test d'unité ne fait que créer une instance de mon ActionResult personnalisé et j'appelle ensuite ExecuteResult. Malheureusement dans la méthode ExecuteResult de mon objet ActionResult personnalisé, il appelle aussi la méthode ExecuteResult d'un ViewResult que je lui ai passé. À ce moment-là, ça explose. Comment devrais-je me moquer de ces choses pour réussir mon test d'unité?

public class SendToAFriendActionResult : ActionResult 
{ 

    public const string INVALID_CAPTCHA = "You don't appear to have filled out the two words from the security image correctly to prove you're a human. Please try again."; 
    public const string INVALID_MODEL_STATE = "You don't appear to have filled out all the details correctly. Please try again."; 
    public const string CONTACT_FAIL = "Unfortunately we experiend a problem sending the link. Please try again later."; 
    public const string SEND_TO_A_FRIEND_FAIL_KEY = "ContactFail"; 

    private RedirectResult _success; 
    private ViewResult _failure; 
    private readonly SendToAFriendModel _model; 
    private readonly bool _captchaValid; 
    private readonly MessageBuilderServiceBase _mbs; 

    public RedirectResult Success 
    { 
     get { return _success; } 
     set { _success = value; } 
    } 

    public ViewResult Failure 
    { 
     get { return _failure; } 
     set { _failure = value; } 
    } 

    public SendToAFriendActionResult(RedirectResult success, ViewResult failure, SendToAFriendModel model, bool captchaValid, MessageBuilderServiceBase mbs) 
    { 
     _success = success; 
     _failure = failure; 
     _model = model; 
     _captchaValid = captchaValid; 
     _mbs = mbs; 
    } 

    public override void ExecuteResult(ControllerContext context) 
    { 

     if (!_captchaValid) 
     { 
      Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_CAPTCHA; 

      // On reaching this point I receive the error 
      // Object reference not set to an instance of an object 
      // as the MVC framework calls FindView 
      Failure.ExecuteResult(context); 
      return; 
     } 

     if (!context.Controller.ViewData.ModelState.IsValid) 
     { 
      Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_MODEL_STATE; 
      Failure.ExecuteResult(context); 
      return; 
     } 

     _mbs.RecipientEmailAddress = _model.EmailRecipient; 
     _mbs.SendersName = _model.SendersName; 
     _mbs.Url = _model.URL; 
     var result = _mbs.sendMessage(); 

     if (!result) 
     { 
      Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = CONTACT_FAIL; 
      Failure.ExecuteResult(context); 
      return; 
     } 

     Success.ExecuteResult(context); 
    } 
} 

est ici le début de mon test unitaire ...

 IMessageService _emailMessageSerivce; 
     IGalleryRepository _repository; 

     var stfModel = new SendToAFriendModel 
     { 
      SendersName = "Someone", 
      URL = "http://someurl.com", 
      EmailRecipient = "[email protected]" 
     }; 

     var failure = new ViewResult() {ViewName ="SendToFriend"}; 
     const bool captchaValid = false; 
     var fakeControlllerContext = MockRepository.GenerateStub<ControllerContext>(null); 

     var stf = new SendToAFriendActionResult(null, failure, stfModel, captchaValid, null); 
     stf.ExecuteResult(fakeControlllerContext); 

J'ai mis des commentaires dans le SUT pour montrer était le problème se produit.

Je sais que je devrais stubbing/se moquant d'une certaine manière, mais je ne peux pas sembler résoudre ce problème.

Répondre

0

De ASP.NET MVC 2 En action (co-auteur par Jimmy Bogard):

En prenant ce code difficile à tester sur d'une action et de le mettre dans la méthode d'exécution Un résultat d'action, vous assurer que les actions deviennent nettement plus facile à tester unitaire. C'est parce que quand vous test unitaire d'une action , vous prétendez le résultat type d'action que les retours d'action et l'état du résultat de l'action. La méthode Execute du résultat de l'action est pas exécuté dans le cadre du test unitaire .

Les tests unitaires sont conçus pour isoler le comportement et les problèmes. Vous mélangez les problèmes en appelant ExecuteResult depuis votre action personnalisée. Au lieu de cela, je devrais le SendToAFriendActionResult retourner le ActionResult réel (Échec ou réussite):

public ActionResult GetAction(..) 
{ 
    ActionResult result; 
    //logic here to determine which ActionResult to return 
    return result; 
} 

Dans votre contrôleur:

public ViewResult SendToAFriend() 
    { 
     return SendToAFriendActionResult(null, failure, stfModel, captchaValid, null) 
      .GetAction(); 
    } 

Cette méthode permettra au framework MVC de faire son travail et isole ces préoccupations en dehors de votre ActionResult personnalisé. Votre test doit affirmer que le bon type d'action, l'échec ou le succès, est retourné en fonction des paramètres définis entrer.

+0

Thaks Dave, qui est parfaitement logique. Je suivais l'exemple de Jimmy à partir du code attaché à la vidéo MvcConf qu'il présentait, où il appelle effectivement ExecuteMethod du succès et de l'échec ActionResults dans la méthode ExecuteMethod du ActionResult initial. Mais cette solution est bien meilleure.Tout fonctionne comme un charme. J'ai en fait les deux versions du MVC dans des livres d'action. Peut-être que je devrais revenir en arrière et faire un peu plus d'étude :) –

+0

Il est * juste arrivé * que je lisais le chapitre sur les tests et les actionsResults personnalisés aujourd'hui. Sérendipité! –