I am creating a test method in Asp.Net MVC 5. I am using following code to test my registration method
[TestMethod]
public async Task Registration_Test_For_Password_And_Confirm_Password_Mismatch()
{
// Arrange
var registerationModel = new RegisterViewModel() { UserName = "abc", Password = null, ConfirmPassword = "jjk", Email = "nitin.daiya#sunarctechnologies.com" };
// Validate model state start
var validationContext = new ValidationContext(registerationModel, null, null);
Validator.TryValidateObject(registerationModel, validationContext, validationResults);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
}
// Validate model state end
// Act
var result = await controller.Register(registerationModel) as ViewResult;
// Assert
Assert.AreEqual("", result.ViewName);
Assert.IsFalse(controller.ModelState.IsValid);
}
Above code is working fine if I pass empty values for the Required fields.
If I try to check any other validation it is not showing the error message and test fails each time.
Following is my ViewModel
public class RegisterViewModel
{
public string UserId { get; set; }
[Required]
[System.Web.Mvc.Remote("IsUserNameAvailable", "Account", ErrorMessage = "That UserName is already taken.")]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[EmailAddress]
[System.Web.Mvc.Remote("IsEmailAvailable", "Account", ErrorMessage = "That email is already taken.")]
[Display(Name = "Email")]
public string Email { get; set; }
}
Any help will be appreciated
EDIT: Solved the issue by changing Validator.TryValidateObject(registerationModel, validationContext, validationResults); to Validator.TryValidateObject(registerationModel, validationContext, validationResults,true); this. True is set for validateAllProperties property to check all validation.
It still not testing the remote validation. Any clue??
Validator.TryValidateObject(registerationModel, validationContext, validationResults)
This line will by default only validate required properties. To validate all properties, you need to set the validateAllProperties parameter to true:
Validator.TryValidateObject(registerationModel, validationContext, validationResults, true);
(Adding this as it wasn't immediately clear that this question had an answer)
Related
I have created a simple project with login and verification. However, I don't know why I cannot use gmail to register the program. During I put my gmail account for registration, I will met this error.
System.Data.Entity.Infrastructure.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.'
During testing this project, I have use hotmail account yao0529#live.com and gmail account yuky0529#gmail.com to register. The hotmail account is register success but gmail cannot. Both of the information for register are exactly same except email.
The code below is normal data class
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace Food_Founder.Models
{
[MetadataType(typeof(UserMetaData))]
public partial class User
{
public string ConfirmPassword { get; set; }
}
public class UserMetaData
{
[Display(Name = "First Name")]
[Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
public string LastName { get; set; }
[Display(Name = "Email ID")]
[Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Display(Name = "Date Of Birth")]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public Nullable<System.DateTime> DateOfBirth { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
[DataType(DataType.Password)]
[MinLength(8, ErrorMessage = "Minimum 8 characters or numbers are required")]
public string Password { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password is not match")]
public string ConfirmPassword { get; set; }
}
}
and this is another normal data class
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Food_Founder.Models
{
using System;
using System.Collections.Generic;
public partial class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public string Password { get; set; }
public bool IsEmailVerified { get; set; }
public System.Guid ActivationCode { get; set; }
}
}
The picture below is my database details.
Below the code is my controller class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Web;
using System.Web.Mvc;
using Food_Founder.Models;
namespace Food_Founder.Controllers
{
public class UserController : Controller
{
//Registration
[HttpGet]
public ActionResult Registration()
{
return View();
}
//Post Registration
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Registration([Bind(Exclude = "IsEmailVerified,ActivationCode")]User user)
{
//Model Validation
bool Status = false;
string message = "";
if (ModelState.IsValid)
{
#region Email is already exist
var isExist = IsEmailExist(user.Email);
if (isExist)
{
ModelState.AddModelError("EmailExist", "Email already exist");
return View(user);
}
#endregion
#region Generate Activation Code
user.ActivationCode = Guid.NewGuid();
#endregion
#region Password Hashing
user.Password = Crypto.Hash(user.Password);
user.ConfirmPassword = Crypto.Hash(user.ConfirmPassword);
#endregion
user.IsEmailVerified = false;
#region Save Data to Database
using (myDatabaseEntities myDatabase = new myDatabaseEntities())
{
myDatabase.Users.Add(user);
myDatabase.SaveChanges();
//Send Email to User
SendVerificationLinkEmail(user.Email, user.ActivationCode.ToString());
message = "Registration successfully done. Account activation link" +
"has been send to your Email:" + user.Email + "Please go check and activate your account";
Status = true;
}
#endregion
}
else
{
message = "Invalid Request";
}
ViewBag.Message = message;
ViewBag.Status = Status;
return View(user);
}
//Verify Email
//Verify Email Link
//Login
//Login POST
//Logout
[NonAction]
public Boolean IsEmailExist(string email)
{
using (myDatabaseEntities myDatabase = new myDatabaseEntities())
{
var v = myDatabase.Users.Where(a => a.Email == email).FirstOrDefault();
return v != null;
}
}
[NonAction]
public void SendVerificationLinkEmail(string email, string activationCode)
{
var verifyUrl = "/User/VerifyAccount/" + activationCode;
var link = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, verifyUrl);
var fromEmail = new MailAddress("yukwokyao2#gmail.com", "yukwokyao");
var toEmail = new MailAddress(email);
var fromEmailPassword = "********";
string subject = "Your account is successfully created!";
string body = "<br/><br/>We are excited to tell you that your FoodFounder account is" +
"successfully created. Please click the below link to verify your FoodFounder account" +
"<a href = '" + link + "' >" + link + "</a>";
var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromEmail.Address, fromEmailPassword)
};
using (var message = new MailMessage(fromEmail, toEmail)
{
Subject = subject,
Body = body,
IsBodyHtml = true
})
smtp.Send(message);
}
}
}
And this is my database.
CREATE TABLE [dbo].[User] (
[UserID] INT IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[FirstName] VARCHAR (50) NOT NULL,
[LastName] VARCHAR (50) NOT NULL,
[Email] VARCHAR (256) NOT NULL,
[DateOfBirth] DATE NULL,
[Password] NVARCHAR (MAX) NOT NULL,
[IsEmailVerified] BIT NOT NULL,
[ActivationCode] UNIQUEIDENTIFIER NOT NULL,
PRIMARY KEY CLUSTERED ([UserID] ASC)
);
GO
SET IDENTITY_INSERT [dbo].[User] ON
Here I met the new problem is when I set the auto-increment for database primary key, it show this error
SQL70001 this statement is not recognized in this context
This error is showed as there is a red line under the SET. Plus, I can't find any database script for set the BuildAction as None
Anywhere, if anything I miss put please inform me ya.. Thank you.
How do I test DataType.EmailAddress?
I have a Customer model with an Email property with the following data annotations to validate:
[StringLength(100)]
[DataType(DataType.EmailAddress, ErrorMessage = "Email must be a valid email address")]
[Display(Name = "Email")]
[Required(ErrorMessage = "Email is required")]
public string email { get; set; }
I am writing unit tests to test validations. I have figured out how to test required and string length.
Here's my method that catches other errors, but doesn't handle the DataType validations:
private List<ValidationResult> ValidateModel<T>(T model)
{
var context = new ValidationContext(model, null, null);
var result = new List<ValidationResult>();
var valid = Validator.TryValidateObject(model, context, result, true);
return result;
}
I call it in a test method:
[TestMethod]
public void Invalid_email_addresses_throw_errors()
{
var model = new Models.Customer();
model.email = "";
var results = ValidateModel(model);
Assert.IsTrue(results.Any(v => v.ErrorMessage == "Email is required"));
}
How do I test DataType.EmailAddress - passing in an invalid value and receiving the an error as a result?
Use the [EmailAddress] DataTypeAttribute.
EmailAddressAttribute is derived from DataTypeAttribute and overrides the IsValid method which is what checks that the value is in fact a valid email.
Using
[DataType(DataType.EmailAddress, ErrorMessage = "Email must be a valid email address")]
does not do anything for email validation.
If you inspect the source code for DataTypeAttribute you will realize that it is primarily a base attribute used to create custom and targeted validation attribute.
The DataTypeAttribute in the original question is being used incorrectly.
There is no other solution than to use the EmailAddressAttribute as demonstrated below in the following unit test.
[TestClass]
public class UnitTestExample {
[TestMethod]
public void Invalid_email_addresses_throw_errors() {
//Arrange
var badEmail = "1234_)(";
var subject = new Customer { email = badEmail };
//Act
var results = ValidateModel(subject);
//Assert
Assert.IsTrue(results.Count > 0);
Assert.IsTrue(results.Any(v => v.MemberNames.Contains("email")));
}
public class Customer {
[StringLength(100)]
[Display(Name = "Email")]
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Email must be a valid email address")]
public string email { get; set; }
}
private List<ValidationResult> ValidateModel<T>(T model) {
var context = new ValidationContext(model, null, null);
var result = new List<ValidationResult>();
var valid = Validator.TryValidateObject(model, context, result, true);
return result;
}
}
I'm making a website with MVC5 ASP.NET.
I'm using Identity framework 2.0 implement class with properties such as passwordhash, username, email, emailconfirmed and so on. I'm using userManager.ChangePassword(user.Id, Oldpassword, Newpassword);, but i can't figure out how i should get a password from a user as plain text (string)
[HttpPost]
public ActionResult ChangePassword(AspNetUsersViewModel userView)
{
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, "123456789", userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
As now I'm i have hardcoded users current password "123456789" to test if it works, and it does.
I hope you guys can help.
Add password input to the View inside the form tag
<input type="password" id= "userNewPassword" name="userNewPassword">
Pass the userNewPasswor as string to the controller after the userView and the pass it to the UserManager
[HttpPost]
public ActionResult ChangePassword(
AspNetUsersViewModel userView,
string userNewPassword){
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, userNewPassword , userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
Note: the Best way is to Modify the userView and add the userNewPassword to the model
Update:
in the visual studio 2013 the if you used the asp.net default template you will find the flowing class
public class ChangePasswordBindingModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
in ASP Web API i use the individual accounts to register the user. I have implemented this functionality following this official tutorial:
Web API Individual Accounts
Now i want be able to assign a Role to the user: when is executed the Register method i want pass also the role in the POST request:
{
"UserName": "Alice",
"Password": "password123",
"ConfirmPassword": "password123",
"Role": "admin" -> i want add this
}
and the Register method must value the database's table properly.
How can i handle the roles in the web api individual accounts? There's any tutorial?
Thanks
Just add the following line in your code after you have created the user and the appropriate role
var roleresult = UserManager.AddToRole(currentUser.Id, "RoleName");
You should also add the Role property to your view model (it is normally called RegisterBindingModel) which is passed to the Register method.
Update
Here's a complete example how a Register method could look like (RoleEntity and UserEntity are my implementations of the appropriate classes in Identity, but this will also work with your custom implementation)
public async Task<IdentityResult> RegisterAsync(RegisterViewModel model)
{
var user = new UserEntity { UserName = model.UserName };
var role = model.Role.ToString();
if (await _roleManager.FindByNameAsync(role) == null)
{
var roleResult = await _roleManager.CreateAsync(new RoleEntity(role));
if (roleResult != IdentityResult.Success)
{
return roleResult;
}
}
var result = await _userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return result;
}
var addToRoleResult = await _userManager.AddToRoleAsync(user.Id, role);
return !addToRoleResult.Succeeded ? addToRoleResult : IdentityResult.Success;
}
My RegisterViewModel looks something like this
public class RegisterViewModel
{
[Required(ErrorMessage="This field is required")]
[Display(Name = "User name", Prompt="Please enter user name...")]
public string UserName { get; set; }
[Required(ErrorMessage = "This field is required")]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password", Prompt="Please enter password...")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password", Prompt = "Please enter confirm password...")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required(ErrorMessage = "This field is required")]
[Display(Name = "Role")]
public Roles Role { get; set; }
}
I am trying to set-up a remote validation similar to the one in this example:
Example
My application has a twist however, my form elements are dynamically generated, therefore this tag:
[Remote("doesUserNameExist", "Account", HttpMethod = "POST", ErrorMessage = "User name already exists. Please enter a different user name.")]
is not set in stone, I need to vary the ErrorMessage for example and preferably vary the action. Is it possible, or would you suggest taking the long-way, meaning to implement the whole ajax validation on my own.
Any suggestions are appreciated.
If you need to have a dynamic error message then you could return this as string from your validation action:
public ActionResult DoesUserNameExist(string username)
{
if (Exists(uasername))
{
string errorMessage = "Some dynamic error message";
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
And if you need even more flexibility such as invoking dynamic dynamic actions, then you're better of rolling your custom validation solution instead of relying on the built-in Remote attribute.
You can inherit from RemoteAttribute and make it fetch the required values from a service or factory according to your own logic. Here is an example:
[AttributeUsage(AttributeTargets.Property)]
public class MyRemoteAttribute : RemoteAttribute
{
public MyRemoteAttribute(Type type, string propertyName)
: base(MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName).Action, MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName).Controller)
{
var data = MyRemoteAttributeDataProvider.GetAttributeData(type,propertyName);
base.ErrorMessage = data.ErrorMessage;
base.HttpMethod = data.HttpMethod;
}
}
public static class MyRemoteAttributeDataProvider
{
public static RemoteAttributeData GetAttributeData(Type type
, string propertyName)
{
//this is where you are going to implement your logic im just implementing as an example
//you can pass in a different type to get your values. For example you can pass in a service to get required values.
//property specific logic here, again im going to implement to make this
//specification by example
var attrData = new RemoteAttributeData();
if(propertyName == "MyOtherProperty")
{
attrData.Action = "MyOtherPropertyRelatedAction";
attrData.Controller = "MyOtherPropertyRelatedController";
attrData.ErrorMessage = "MyOtherPropertyRelated Error Message";
attrData.HttpMethod = "POST";
}
else
{
attrData.Action = "UserNameExists";
attrData.Controller = "AccountController";
attrData.ErrorMessage = "Some Error Message";
attrData.HttpMethod = "POST";
}
return attrData;
}
}
public class RemoteAttributeData
{
public string Controller { get; set; }
public string Action { get; set; }
public string HttpMethod { get; set; }
public string ErrorMessage { get; set; }
}
And this is how you are supposed to use is:
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
[MyRemote(typeof(RegisterViewModel),"UserName")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[MyRemote(typeof(RegisterViewModel),"MyOtherProperty")]
public string MyOtherProperty { get; set; }
}
As I also mentioned above at the commentary. You should specialize that provider according to your needs.
I hope this helps.
UPDATE:
I update the implementation based on your comment, so that it takes a property name and does some property name specific wiring.