75 votes

Tests unitaires sur la validation MVC

Comment puis-je tester que mon contrôleur de l'action est de mettre la correction d'erreurs dans le ModelState lors de la validation d'une entité, lorsque je suis en utilisant DataAnnotation de validation dans MVC 2 Extrait 1?

Un peu de code pour illustrer. Tout d'abord, l'action:

    [HttpPost]
    public ActionResult Index(BlogPost b)
    {
        if(ModelState.IsValid)
        {
            _blogService.Insert(b);
            return(View("Success", b));
        }
        return View(b);
    }

Et voici un échec de test de l'unité qui, je pense, devrait passer mais n'est-ce pas (à l'aide de MbUnit & Moq):

[Test]
public void When_processing_invalid_post_HomeControllerModelState_should_have_at_least_one_error()
{
    // arrange
    var mockRepository = new Mock<IBlogPostSVC>();
    var homeController = new HomeController(mockRepository.Object);

    // act
    var p = new BlogPost { Title = "test" };            // date and content should be required
    homeController.Index(p);

    // assert
    Assert.IsTrue(!homeController.ModelState.IsValid);
}

Je suppose qu'en plus de cette question, doit - je les tests de validation, et que je devrais tester de cette façon?

192voto

ARM Points 1909

La haine de nécro un vieux post, mais j'ai pensé que je pourrais ajouter mes propres pensées (depuis que j'ai juste eu ce problème et a couru à travers ce post en cherchant la réponse).

  1. Ne faites pas de test de validation de votre contrôleur de tests. Soit vous faites confiance à la MVC de validation ou écrire votre propre (c'est à dire ne pas tester d'autres de code, des test de code)
  2. Si vous ne voulez pas de test de validation est en train de faire ce que vous attendez, de le tester dans votre modèle de tests (je fais cela pour un couple de mes plus complexe regex validations).

Ce que vous voulez vraiment tester ici est que votre contrôleur est-ce que vous attendez à faire lors de la validation échoue. C'est votre code et vos attentes. Des tests, il est facile une fois que vous vous rendez compte que tout ce que vous voulez tester:

[test]
public void TestInvalidPostBehavior()
{
    // arrange
    var mockRepository = new Mock<IBlogPostSVC>();
    var homeController = new HomeController(mockRepository.Object);
    var p = new BlogPost();

    homeController.ViewData.ModelState.AddModelError("Key", "ErrorMessage"); // Values of these two strings don't matter.  
    // What I'm doing is setting up the situation: my controller is receiving an invalid model.

    // act
    var result = (ViewResult) homeController.Index(p);

    // assert
    result.ForView("Index")
    Assert.That(result.ViewData.Model, Is.EqualTo(p));
}

87voto

Giles Smith Points 1335

J'avais eu le même problème, et après la lecture de Pauls réponse et commentaire, j'ai cherché un moyen de manuellement la validation du modèle de vue.

J'ai trouvé ce tutoriel qui explique comment valider manuellement un ViewModel qui utilise DataAnnotations. On a extrait de code est vers la fin du post.

J'ai modifié le code légèrement - dans le tutoriel, le 4ème paramètre de la TryValidateObject est omis (validateAllProperties). Afin d'obtenir toutes les annotations pour Valider, ce doit être défini à true.

De plus j'ai refait le code dans une méthode générique, pour effectuer des tests de ViewModel de validation simple:

private static void ValidateViewModel<VM, C>(VM viewModelToValidate, C controller) where C:Controller
{
    var validationContext = new ValidationContext(viewModelToValidate, null, null);
    var validationResults = new List<ValidationResult>();
    Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
    foreach (var validationResult in validationResults)
    {
        controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
    }
}

Jusqu'à présent cela a vraiment bien fonctionné pour nous.

7voto

Paul Alexander Points 17611

Lorsque vous appelez la méthode homeController.Index dans votre test, vous n'utilisez aucun des frameworks MVC qui déclenchent la validation afin que ModelState.IsValid soit toujours à true. Dans notre code, nous appelons une méthode de validation helper directement dans le contrôleur plutôt que d'utiliser la validation ambiante. Je n'ai pas beaucoup d'expérience avec les DataAnnotations (nous utilisons NHibernate.Validators) peut-être que quelqu'un d'autre peut vous conseiller sur la procédure à suivre pour appeler Validate depuis votre contrôleur.

3voto

Darren Points 194

Je faisais des recherches à ce sujet aujourd'hui et j'ai trouvé ce billet de blog de Roberto Hernández (MVP) qui semble fournir la meilleure solution pour renvoyer les validateurs pour une action du contrôleur lors des tests unitaires. Cela mettra les erreurs correctes dans ModelState lors de la validation d'une entité.

2voto

gustavogb Points 2681

J'utilise ModelBinders dans mes cas de test pour pouvoir mettre à jour la valeur de model.IsValid.

 var form = new FormCollection();
form.Add("Name", "0123456789012345678901234567890123456789");

var model = MvcModelBinder.BindModel<AddItemModel>(controller, form);

ViewResult result = (ViewResult)controller.Add(model);
 

Avec ma méthode MvcModelBinder.BindModel comme suit (essentiellement le même code utilisé en interne dans le framework MVC):

         public static TModel BindModel<TModel>(Controller controller, IValueProvider valueProvider) where TModel : class
        {
            IModelBinder binder = ModelBinders.Binders.GetBinder(typeof(TModel));
            ModelBindingContext bindingContext = new ModelBindingContext()
            {
                FallbackToEmptyPrefix = true,
                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)),
                ModelName = "NotUsedButNotNull",
                ModelState = controller.ModelState,
                PropertyFilter = (name => { return true; }),
                ValueProvider = valueProvider
            };

            return (TModel)binder.BindModel(controller.ControllerContext, bindingContext);
        }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X