Originally posted on GeeksWithBlogs.net on May 26th, 2013.
I have requirement (specification) that the password has to be at least 7 characters long and contain a special character ( ~@#$ &*()-_+=) which are all the specials in the number keyboard row. I found that using the RegularExpression DataAnnotations is really slick, but there was a point that caught me for a while.
The problem is that @".([!@#$&()-+=]+).$" works in the UI, but fails in the unit test. @"[!@#$&()-+=]+" works in the test, but not in the UI (the MVC unobtrusive library wants a full match). When I removed the ‘()’ it works in both. So my solution, after losing some time, is to add the escape before the ‘-’. This works: ^.(?=.[!@#$%^&\(\)_\-+=]).$ (thanks to my co-worker Jason).
Here comes the code:
The Model for the MVC binding. Focus on the RegEx for NewPassword:
public class PasswordResetModel
{
/// <summary>
/// Gets or sets the new password.
/// It must be at least 7 characters and have at least one special character.
/// </summary>
/// <value>
/// The new password.
/// </value>
[Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
ErrorMessageResourceName = "NewPasswordRequiredMessage")]
[StringLength(100, ErrorMessageResourceType = typeof(AuthenticationModelsResource),
ErrorMessageResourceName = "PasswordTooShortErrorMessage", MinimumLength = 7)]
[RegularExpression(@"^.*(?=.*[!@#$%^&*\\(\\)_\\-+=\]).*$",
ErrorMessageResourceType = typeof(AuthenticationModelsResource),
ErrorMessageResourceName = "PasswordNotStrongEnoughMessage")]
[DataType(DataType.Password)]
[Display(ResourceType = typeof(AuthenticationModelsResource), Name = "NewPassword"]
public string NewPassword { get; set; }
/// <summary>
/// Gets or sets the confirm password.
/// </summary>
/// <value>
/// The confirm password.
/// </value>
[DataType(DataType.Password)]
[Display(ResourceType = typeof(AuthenticationModelsResource), Name = "ConfirmNewPassword")]
[Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
ErrorMessageResourceName = "ConfirmNewPasswordRequiredMessage")]
[Compare("NewPassword", ErrorMessageResourceType = typeof(AuthenticationModelsResource),
ErrorMessageResourceName = "PasswordCompareErrorMessage")]
public string ConfirmPassword { get; set; }
}
The Unit test:
[TestMethod,TestCategory("HomeController")]
public void It_Should_Validate_Password_Doesnt_Have_A_Special_Character()
{
PasswordResetViewModel vm = new PasswordResetViewModel("match", 10)
{
PasswordResetModel =
{
NewPassword = "test1122",
ConfirmPassword = "test1122"
}
};
((ChangePasswordModel)vm.PasswordResetModel).OldPassword = "test1";
this.Controller.PasswordReset(vm);
// assert
ValidateViewModel(vm.PasswordResetModel, this.Controller);
Should.BooleanAssertionExtensions.ShouldBeTrue(this.Controller.ModelState.Values.Last()
.Errors.Any(e => e.ErrorMessage == AuthenticationModelsResource.PasswordNotStrongEnoughMessage));
}
The ValidateViewModel method:
protected static void ValidateViewModel<TVm, TC>(TVm viewModelToValidate, TC controller) where TC : Controller
{
var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModelToValidate, viewModelToValidate.GetType());
var validator = ModelValidator.GetModelValidator(modelMetadata, controller.ControllerContext);
var result = validator.Validate(viewModelToValidate);
foreach (var validationResult in result)
{
if (!string.IsNullOrWhiteSpace(validationResult.MemberName))
{
controller.ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
}
else
{
controller.ModelState.AddModelError(string.Empty, validationResult.Message);
}
}
}
The MVC Unobtrusive Validation library wants a complete match (from the source code):
$jQval.addMethod("regex", function (value, element, params) {
var match;
if (this.optional(element)) {
return true;
}
match = new RegExp(params).exec(value);
return (match && (match.index === 0) && (match\[0\].length === value.length));
});
Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout (I'm at $95.73!), I pledge to turn off ads)
Also check out my Resources Page for referrals that would help me.