Mit ASP.NET MVC 2 ist es ja nun möglich über DataAnnotations oder mit eigenen ModelValidatoren eine schnelle und einfache Validierung der Eingabedaten vorzunehmen. Dieses im Unit Test zu machen ist jedoch out of the Box nicht möglich.
Thomas Bandt hat dazu mal einen Blog Beitrag geschrieben. Der sich jedoch rein auf DataAnnotations bezieht und keine anderen ModelValdator berücksichtigt. Dies ist natürlich nicht ganz so gut. Desweiteren funktioniert sie nur unter .NET 4 und man kann damit nicht den Programmfluss im Controller selbst testen (was macht dieser bei invaliden Daten?).
Hier stelle ich eine Extension-Method vor die ohne diese Schwachpunkte auskommt.
public static class ControllerValidationExtensions
{
public static void ValidateModel(this Controller controller, object model)
{
ValidateModel(controller, model, null);
}
public static void ValidateModel(this Controller controller, object model, string prefix)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
EnsureControllerContext(controller);
foreach (ModelValidationResult validator in model.GetModelValidator(controller).Validate(model))
{
controller.AddModelError(prefix, validator);
}
}
private static void EnsureControllerContext(Controller controller)
{
if (controller.ControllerContext==null)
{
controller.ControllerContext=new ControllerContext();
}
}
private static ModelValidator GetModelValidator(this object model, Controller controller)
{
return ModelValidator.GetModelValidator(
ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
controller.ControllerContext
);
}
private static void AddModelError(this Controller controller, string prefix, ModelValidationResult validator)
{
controller.ModelState.AddModelError(
CreateSubPropertyName(prefix, validator.MemberName),
validator.Message);
}
private static string CreateSubPropertyName(string prefix, string propertyName)
{
if (string.IsNullOrEmpty(prefix))
{
return propertyName;
}
if (string.IsNullOrEmpty(propertyName))
{
return prefix;
}
return (prefix + "." + propertyName);
}
}
Ruft man dies nun im Testlauf vor dem Aufruf der Controller-Action auf, so ist auch der ModelState mit den entsprechenden Fehlern gefüllt. Auch werden die im ASP.NET MVC registrierten MetaDataProvider und ModelValidatoren dafür verwenden. Eine eventuelle Registrierung dieser muss natürlich im Test erfolgen.
Nun ein kleines, einfaches Beispiel.
Gegeben ist folgendes ViewModel
public class UserRegistrationModel
{
[Required]
public string Username { get; set; }
[Required]
public string EMail { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string PasswordConfirmation { get; set; }
}
und dieser Controller, der bewusst einfach gehalten ist, damit schneller erfasst werden kann.
public class UserRegistrationController : RegularlyController
{
private readonly IUserRegistration userRegistration;
private readonly IRegularlyContext regularlyContext;
public UserRegistrationController(IUserRegistration userRegistration, IRegularlyContext regularlyContext)
{
this.userRegistration = userRegistration;
this.regularlyContext = regularlyContext;
}
public ActionResult Index()
{
return View(new UserRegistrationModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(UserRegistrationModel model)
{
if (ModelState.IsValid)
{
/* magie */
return RedirectToAction("ThankYou");
}
return View(model);
}
}
Der Ablauf eines typischen Test wäre dann folgender
[Fact]
public void SimpleValidationTest()
{
var controller = new UserRegistrationController(null,null);
var postModel = new UserRegistrationModel
{
EMail = "info@der-albert.com",
Password = "password",
PasswordConfirmation = "password"
};
controller.ValidateModel(postModel);
var result = controller.Index(postModel);
result.ShouldBeAnInstanceOf<ViewResult>();
}
Die Validierung muss manuell durchgeführt werden bevor die Action aufgerufen wird. Dies passiert im Framework auch davor. Bei einem Test ohne die Validierung würde immer ein RedirectRouteResult zurückgegeben.
Viel Spaß damit!
Print | posted on Friday, May 14, 2010 5:25 PM
Der Eintrag ist mir etwas Wert