52 Stimmen

Wie kann ich ModelState testen?

Wie kann ich testen Controller.ViewData.ModelState ? Ich würde es vorziehen, es ohne ein Mock-Framework zu tun.

0 Stimmen

Seien Sie genauer. Worauf wollen Sie den Test anwenden?

45voto

Scott Hanselman Punkte 17602

Sie müssen natürlich kein Mock verwenden, wenn Sie das Repository Pattern für Ihre Daten verwenden.

Einige Beispiele: http://www.singingeels.com/Articles/Test_Driven_Development_with_ASPNET_MVC.aspx

// Test for required "FirstName".
   controller.ViewData.ModelState.Clear();

   newCustomer = new Customer
   {
       FirstName = "",
       LastName = "Smith",
       Zip = "34275",    
   };

   controller.Create(newCustomer);

   // Make sure that our validation found the error!
   Assert.IsTrue(controller.ViewData.ModelState.Count == 1, 
                 "FirstName must be required.");

1 Stimmen

Ein virtueller Modifikator für Fehler wäre schön, aber leider gibt es eine Gestaltungssteuer, die mir ein wenig zu hoch erscheint.

0 Stimmen

IMHO ist die bessere Lösung, mvc-Förderer zu verwenden. Auf diese Weise erhalten Sie mehr realistisches Verhalten Ihres Controllers, sollten Sie Modell-Validierung zu seinem Schicksal liefern - Attribut-Validierungen. Unten Post ist dies zu beschreiben ( stackoverflow.com/a/5580363/572612 ).

1 Stimmen

Der von Scott verlinkte Artikel scheint defekt zu sein, kann er woanders gefunden werden?

38voto

VaSSaV Punkte 371
//[Required]
//public string Name { get; set; }
//[Required]
//public string Description { get; set; }

ProductModelEdit model = new ProductModelEdit() ;
//Init ModelState
var modelBinder = new ModelBindingContext()
{
    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                      () => model, model.GetType()),
    ValueProvider=new NameValueCollectionValueProvider(
                        new NameValueCollection(), CultureInfo.InvariantCulture)
};
var binder=new DefaultModelBinder().BindModel(
                 new ControllerContext(),modelBinder );
ProductController.ModelState.Clear();
ProductController.ModelState.Merge(modelBinder.ModelState);

ViewResult result = (ViewResult)ProductController.CreateProduct(null,model);
Assert.IsTrue(result.ViewData.ModelState["Name"].Errors.Count > 0);
Assert.True(result.ViewData.ModelState["Description"].Errors.Count > 0);
Assert.True(!result.ViewData.ModelState.IsValid);

0 Stimmen

Ich mag diese Methode sehr. Wie Sie sagen, kann man damit ein Modell, das mit Validierungsattributen versehen wurde, viel besser testen.

0 Stimmen

Sehr praktisch. Ich denke, ich stimme im Allgemeinen mit Leuten, die sagen, dass Sie nicht Ihre Modelleigenschaften testen sollten, da dies ein Teil des Rahmens ist, aber es gibt Zeiten, wenn es kritisch ist, für alle zukünftigen Zeit zu bestätigen, dass Ihr Controller kritische Fehler fängt.

0 Stimmen

Ich versuche, einen gleichwertigen Code für die Web-API zu finden. Wenn jemand weiß, vielleicht könnte er hier aktualisieren?

12voto

Bart Verkoeijen Punkte 14455

Zum Testen der Web-API verwenden Sie die Validieren Sie Methode auf dem Controller:

var controller = new MyController();
controller.Configuration = new HttpConfiguration();
var model = new MyModel();

controller.Validate(model);
var result = controller.MyMethod(model);

11voto

Dieses Problem trat bei .NetCore 2.1 auf Hier ist meine Lösung:

Erweiterungsmethode

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace MyExtension
{
    public static void BindViewModel<T>(this Controller controller, T model)
    {
        if (model == null) return;

        var context = new ValidationContext(model, null, null);
        var results = new List<ValidationResult>();

        if (!Validator.TryValidateObject(model, context, results, true))
        {
            controller.ModelState.Clear();
            foreach (ValidationResult result in results)
            {
                var key = result.MemberNames.FirstOrDefault() ?? "";
                controller.ModelState.AddModelError(key, result.ErrorMessage);
            }
        }
    }
}

Ansicht Modell

public class MyViewModel
{
    [Required]
    public string Name { get; set; }
}

Einheitstest

public async void MyUnitTest()
{
    // helper method to create instance of the Controller
    var controller = this.CreateController();

    var model = new MyViewModel
    {
        Name = null
    };

    // here we call the extension method to validate the model
    // and set the errors to the Controller's ModelState
    controller.BindViewModel(model);

    var result = await controller.ActionName(model);

    Assert.NotNull(result);
    var viewResult = Assert.IsType<BadRequestObjectResult>(result);
}

1 Stimmen

THIS! Warum funktioniert DNC2 nicht von Haus aus so :( Ich wollte nur einen Unit-Test machen, der sicherstellt, dass If !ModelState.IsValid, return BadRequest() in einer Controller-Aktion

1 Stimmen

Funktioniert gut, es sei denn, das Modell ist ein IEnumerable. Ich hackte Ihren Code überprüfen, wenn das Modell ein IEnumerable dann durch jedes Modell tun die TryValidationObject Prüfung Schleife ist.

0 Stimmen

@mobese46 schöne Ergänzung!

2voto

abovetempo Punkte 120

So können Sie nicht nur überprüfen, ob der Fehler existiert, sondern auch, ob er genau die gleiche Fehlermeldung hat wie erwartet. Zum Beispiel sind beide Parameter erforderlich, so dass ihre Fehlermeldung als "Erforderlich" angezeigt wird.

Modellauszeichnung:

//[Required]
//public string Name { get; set; }
//[Required]
//public string Description { get; set; }

Unit-Test-Code:

ProductModelEdit model = new ProductModelEdit() ;
//Init ModelState
var modelBinder = new ModelBindingContext()
{
    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                      () => model, model.GetType()),
    ValueProvider=new NameValueCollectionValueProvider(
                        new NameValueCollection(), CultureInfo.InvariantCulture)
};
var binder=new DefaultModelBinder().BindModel(
                 new ControllerContext(),modelBinder );
ProductController.ModelState.Clear();
ProductController.ModelState.Merge(modelBinder.ModelState);

ViewResult result = (ViewResult)ProductController.CreateProduct(null,model);
Assert.IsTrue(!result.ViewData.ModelState.IsValid);
//Make sure Name has correct errors
Assert.IsTrue(result.ViewData.ModelState["Name"].Errors.Count > 0);
Assert.AreEqual(result.ViewData.ModelState["Name"].Errors[0].ErrorMessage, "Required");
//Make sure Description has correct errors
Assert.IsTrue(result.ViewData.ModelState["Description"].Errors.Count > 0);
Assert.AreEqual(result.ViewData.ModelState["Description"].Errors[0].ErrorMessage, "Required");

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X