web-dev-qa-db-fra.com

Test unitaire post-méthode du contrôleur ASP.NET MVC: ModelState.IsValid toujours vrai

J'ai écrit mes premiers tests unitaires pour une application Web ASP.NET MVC. Tout fonctionne bien et cela me donne des informations précieuses, mais je ne peux pas tester les erreurs dans le modèle d'affichage. Le ModelState.IsValid est toujours vrai, même lorsque certaines valeurs ne sont pas remplies (chaîne vide ou nulle).

J'ai déjà lu que la validation du modèle se produit lorsque les données publiées sont mappées au modèle et que vous devez écrire du code pour effectuer la vérification du modèle vous-même:

J'ai essayé les trois exemples fournis dans les pages Web liées, mais cela ne semble pas fonctionner pour moi.

Du code:

Mon modèle d'affichage

...
[Required(ErrorMessageResourceName = "ErrorFirstName", ErrorMessageResourceType = typeof(Mui))]
[MaxLength(50)]
[Display(Name = "Firstname", ResourceType = typeof(Mui))]
public string FirstName { get; set; }
...

Le contrôleur

...
 [HttpPost]
    public ActionResult Index(POSViewModel model)
    {
        Contract contract = contractService.GetContract(model.ContractGuid.Value);

        if (!contract.IsDirectDebit.ToSafe())
        {
            ModelState.Remove("BankName");
            ModelState.Remove("BankAddress");
            ModelState.Remove("BankZip");
            ModelState.Remove("BankCity");
            ModelState.Remove("AccountNr");
        }

        if (ModelState.IsValid)
        {
            ...

            contractValidationService.Create(contractValidation);
            unitOfWork.SaveChanges();

            return RedirectToAction("index","thanks");
        }
        else
        {
            return Index(model.ContractGuid.ToString());
        }
    }

Mon test unitaire

  posViewModel.FirstName = null;
  posViewModel.LastName = "";
 ...
 var modelBinder = new ModelBindingContext()
        {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => posViewModel, posViewModel.GetType()),
            ValueProvider = new NameValueCollectionValueProvider(new System.Collections.Specialized.NameValueCollection(), CultureInfo.InvariantCulture)
        };
        var binder = new DefaultModelBinder().BindModel(new ControllerContext(), modelBinder);
        posController.ModelState.Clear();
        posController.ModelState.Merge(modelBinder.ModelState);

        ActionResult result = posController.Index(posViewModel);

        //Assert
        mockContractValidationService.Verify(m => m.Create(It.IsAny<ContractValidation>()), Times.Never);
        Assert.IsInstanceOfType(result, typeof(ViewResult));

Sur la vue, j'utilise une validation JavaScript discrète, et cela fonctionne.

33
CyclingFreak

J'ai trouvé cette solution: SO: la validation ne fonctionne pas lorsque j'utilise Validator.TryValidateObject combiné avec la solution @Kenneth fournie:

[TestMethod]
    public void test_validation()
    {
        var sut = new POSViewModel();
        // Set some properties here
        var context = new ValidationContext(sut, null, null);
        var results = new List<ValidationResult>();
        TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(POSViewModel), typeof(POSViewModel)), typeof(POSViewModel));

        var isModelStateValid = Validator.TryValidateObject(sut, context, results, true);

        // Assert here
    }

Si vous avez une bibliothèque de classes avec toutes vos ressources, n'oubliez pas de la référencer dans votre projet de test.

9
CyclingFreak

Vous essayez de tester deux choses différentes en même temps. Le contrôleur n'est pas responsable de la validation de l'état du modèle, mais uniquement d'un comportement différent en fonction du résultat de cette validation. Donc, vos tests unitaires pour le contrôleur ne devraient pas essayer de tester la validation, cela devrait être fait dans un test différent. À mon avis, vous devriez avoir trois tests unitaires:

  1. Celui qui vérifie si la validation du modèle est correcte
  2. Celui qui valide si le contrôleur se comporte correctement lorsque modelstate est valide
  3. Celui qui valide si le contrôleur se comporte correctement lorsque modelstate n'est pas valide

Voici comment procéder:

1. validation du modèle

[Test]
public void test_validation()
{
    var sut = new POSViewModel();
    // Set some properties here
    var context = new ValidationContext(sut, null, null);
    var results = new List<ValidationResult>();
    var isModelStateValid =Validator.TryValidateObject(sut, context, results, true);

    // Assert here
}

2.Contrôleur avec état du modèle non valide

[Test]
public void test_controller_with_model_error()
{
    var controller = new PosController();
    controller.ModelState.AddModelError("test", "test");

    ActionResult result = posController.Index(new PosViewModel());

    // Assert that the controller executed the right actions when the model is invalid
}

3.Contrôleur avec état du modèle valide

[Test]
public void test_controller_with_valid_model()
{
    var controller = new PosController();
    controller.ModelState.Clear();

    ActionResult result = posController.Index(new PosViewModel());

    // Assert that the controller executed the right actions when the model is valid
}
85
Kenneth