ASP.NET MVC Controller POST method unit test: ModelState.IsValid toujours vrai

j'ai écrit mes premiers tests unitaires pour un ASP.NET application web MVC. Tout fonctionne bien et il me donne des informations précieuses, mais je ne peux pas tester les erreurs dans le modèle de vue. Le ModelState.IsValid est toujours true, même si certaines valeurs ne sont pas remplies (chaîne vide ou null).

j'ai déjà lu que la validation du modèle se produit lorsque les données postées sont mappées sur le modèle et que vous avez besoin d'écrire du code pour faire vous-même la vérification du modèle:

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

code:

Mon viewmodel

...
[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 le point de vue, je suis en utilisant JavaScript discret de validation, et il fonctionne.

25
demandé sur Community 2014-03-21 18:41:12

2 réponses

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 seulement du comportement différent basé sur le 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. un modèle qui vérifie si la validation du modèle est correcte
  2. One cela valide si le controller se comporte correctement quand modelstate est valide
  3. celui qui valide si le contrôleur se comporte correctement quand modelstate est invalide

Voici comment vous pouvez faire cela:

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 modelstate "151940920 invalide"

[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 de 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
}
67
répondu Kenneth 2015-06-27 16:34:33

j'ai trouvé cette solution: donc: la Validation ne fonctionne pas quand j'utilise Validator.TryValidateObject combiné avec la solution @Kenneth fourni:

[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 classe avec toutes vos ressources, n'oubliez pas de le mentionner dans votre projet d'essai.

8
répondu JurgenStillaert 2017-05-23 10:31:15