I am currently experimenting with validation attributes,
and now I am trying to validate my ViewModel which contains an EmailAddress with a custom validation attribute.
public class UserLoginModel
{
[Required]
[EmailAddress]
public string email { get; set; }
[Required]
public string password { get; set; }
public bool rememberMe { get; set; }
}
I have made a unit-test where I give a false email address and try to validate my viewmodel.
[TestMethod]
public void TestingInvalidEmailAddress()
{
UserLoginModel model = new UserLoginModel();
model = GetAValidLoginModel(); //Get a default-model where all parameters are correct
model.email = "thisisnotavalidemail.com";
ValidationContext context = new ValidationContext(model, null, null);
var results = new List<ValidationResult>();
bool validModel= Validator.TryValidateObject(model, context, results);
//This is always true
Assert.IsFalse(validModel);
}
The result of this test is always False.
So I checked my attribute, because I thought I might have made a mistake:
[TestMethod]
public void Email()
{
string email;
var attr = new EmailAddressAttribute();
email = "myemail#domain.com";
Assert.IsTrue(attr.IsValid(email));
email = "thisisnotavalidemail.com";
Assert.IsFalse(attr.IsValid(email)); //If this fails, the test is successfull
}
And that did pass the test, using the exact same email address.
And when I test it in my browser, it also validates correctly.
So why does it not tell me that my email address is invalid in the first test-method?
I found my solution in over here.
Apparently I am just missing an extra parameter.
bool validModel= Validator.TryValidateObject(model, context, results, **true**);
Related
I have added model validator to validate to model. it's covered only required fields but not others.
public static class TestModelHelper
{
public static IList<ValidationResult> Validate(object model)
{
var results = new List<ValidationResult>();
var validationContext = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, validationContext, results, true);
if (model is IValidatableObject)
{
(model as IValidatableObject).Validate(validationContext);
}
return results;
}
}
public class Employee
{
[Key]
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[Required]
[JsonProperty("emailId")]
public string EmailId { get; set; }
}
using below command to generate the code coverage report.
dotnet test --collect:"XPlat Code Coverageā
reportgenerator "-reports:./TestResults/{testresultsId}/coverage.cobertura.xml" "-targetdir:coveragereport" "-"reporttypes:Html"
in this model emailId only covered in code coverage. id and name are not covered.
According to your codes, I don't found any validate attribute for the Name and Id, if you want to test validate result, you should put some validate attributes for them.
More details, you could refer to below codes and try again.
public class Employee
{
[JsonProperty("id")]
[Range(0, 999.99)]
public int Id { get; set; }
[StringLength(100)]
[JsonProperty("name")]
public string Name { get; set; }
[Required]
[JsonProperty("emailId")]
public string EmailId { get; set; }
}
I can't verify and test my database by in memory providers.
for example I set these properties to required :
public abstract class Log
{
#region Properties
public Guid Id { get; set; }
[Required]
public string ClientIp { get; set; }
[Required]
public string Application { get; set; }
[Required]
public string Host { get; set; }
[Required]
public string Path { get; set; }
[Required]
public string Method { get; set; }
[Required]
public string User { get; set; }
[Required]
public string Date { get; set; }
#endregion
}
and this is my DBContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUsers, Role, Guid>, IUnitOfWork
{
private readonly IConfigurationRoot _configuration;
public ApplicationDbContext(IConfigurationRoot configuration)
{
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var useInMemoryDatabase = _configuration[key: "UseInMemoryDatabase"].Equals(value: "true",
comparisonType: StringComparison.OrdinalIgnoreCase);
if (useInMemoryDatabase)
optionsBuilder.UseInMemoryDatabase();
else
optionsBuilder.UseSqlServer(
connectionString: _configuration[key: "ConnectionStrings:ApplicationDbContextConnection"]
, sqlServerOptionsAction: serverDbContextOptionsBuilder =>
{
var minutes = (int) TimeSpan.FromMinutes(3).TotalSeconds;
serverDbContextOptionsBuilder.CommandTimeout(commandTimeout: minutes);
});
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Log>()
.HasKey(c => c.Id);
modelBuilder.Entity<Log>()
.HasDiscriminator<int>(name: "Type")
.HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information))
.HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error));
}
And this is my unit test :
[TestClass]
public class LogRepositoryTest
{
private readonly IServiceProvider _serviceProvider;
public LogRepositoryTest()
{
var services = new ServiceCollection();
services.AddScoped<IUnitOfWork, ApplicationDbContext>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddSingleton(provider => new ConfigurationBuilder()
.AddInMemoryCollection(initialData: new[]
{
new KeyValuePair<string, string>(key: "UseInMemoryDatabase", value: "true"),
})
.Build());
services.AddEntityFrameworkInMemoryDatabase().AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
_serviceProvider = services.BuildServiceProvider();
}
[TestMethod]
public async Task Verify_SaveRequestLog()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
{
context.Set<Log>().Add(new LogRequest());
var result =await context.SaveAllChangesAsync();
Assert.AreEqual(1, result);
}
}
}
But the unit test method always return 1 and passes, meanwhile the empty object of LogRequest must not save anything to database!
How can I determine not null properties for unit test ? In fact how can I enforce unit test to reflect to validation policies ?
Update:
Based on this linke :
Entity Framework Core Issues
that I asked, I got this respond:
EF Core doesn't do any validation of entities beyond what is needed
for internal consistency. Validation is something that could be done
in EF, but experience shows that it is not something that is useful to
many developers because it usually cannot replace either client-side
validation or database validation and there are also other places
where validation can be done more effectively.
Going beyond EF to the database, the in-memory database does not
currently validate nullability (i.e. requiredness) when saving
property values. I will leave this issue open so that we can discuss
as a team whether this is something we should add.
Also, if the intent is test with an in-memory database as an
approximation for a relational database, then you might want to
consider using SQLite in in-memory mode. See
https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/index
for more information.
Based on this linke :
Entity Framework Core Issues
that I asked, I got my answer :
class MyContext : DbContext
{
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}
Im using VS2012, ASP MVC4.
I want validate the new registered user email using this on my model:
[DataType(DataType.EmailAddress)]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
This works, but I want block some email sites how: 10minutemail, etc.. I want searching a option that allows me config the DataType Email Anotation or extends it..
I thinks this can be more clean that a big regex that validate all.
Thanks a lot
You can create a custom email validation attribute that wraps over EmailAddressAttribute you are using now:
public class CustomEmailValidationAttribute : ValidationAttribute
{
private string[] blockedProviders = new[]
{
"10minutemail.com",
"some-temporary-email.net"
};
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var emailValidationAttribute = new EmailAddressAttribute();
if (!emailValidationAttribute.IsValid(value))
return new ValidationResult("Invalid email");
bool isBlocked = blockedProviders.Any(pr => ((string)value)
.EndsWith(pr, StringComparison.InvariantCultureIgnoreCase));
if (isBlocked)
return new ValidationResult("Email provider is not allowed");
return ValidationResult.Success;
}
}
Then you can mark email fields with [CustomEmailValidation] instead of [EmailAddress].
I have this controller:
[HttpPost]
public ActionResult Create(Company company)
{
// try to save the record
if (ModelState.IsValid)
{
// create the command
var command = new CreateOrUpdateCompanyCommand
{
CompanyId = company.CompanyId,
Code = company.Code,
Name = company.Name
};
// check for errors
IEnumerable<ValidationResult> errors = _commandBus.Validate(command);
ModelState.AddModelErrors(errors);
if (ModelState.IsValid)
{
var result = _commandBus.Submit(command);
if (result.Success)
return RedirectToAction("Index");
}
}
// if fail
return View("Create", company);
}
I have this test for NUnit:
[Test]
public void Create()
{
const string expectedRouteName = "Index";
// Mock the arguments
var mockRepository = Substitute.For<ICompanyRepository>();
var mockProcessor = Substitute.For<ICommandBus>();
// Arrange
var controller = new CompanyController(mockProcessor, mockRepository);
// Act
var company = new Company
{
Code = "XXXXXXX",
CompanyId = 1,
Name = "Sample company"
};
var result = controller.Create(company) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should return a ViewResult");
Assert.AreEqual(expectedRouteName, result.RouteValues["action"],
"View name should have been '{0}'", expectedRouteName);
}
This is the Model:
public class Company
{
[Key]
public int CompanyId { get; set; }
[Required(ErrorMessage = "* (xxxx)")]
[MaxLength(7)]
[RegularExpression("^([A-Z0-9]{7})$", ErrorMessage = "xxx")]
[Display(Name = "Code")]
public string Code { get; set; }
[Required(ErrorMessage = "*")]
[MaxLength(40)]
[Display(Name = "Name")]
public string Name { get; set; }
}
When I Run the test, this function always return false: var result = _commandBus.Submit(command); and the test fails.
I don't know how to test it? Should I mock the _commandBus and set it to return true? I tried in this way but unsuccessfully:
var mockCommand = Substitute.For<ICommand>();
mockProcessor.Submit(mockCommand).Success.Returns(true);
To create this project I have got inspiration from this http://efmvc.codeplex.com/
Any advise to me? Thanks.
The command you pass to mockProcessor.Submit(mockCommand).Success.Returns(true) needs to be the same one that the code-under-test passes. As the method news up its own command this will never be the case.
Easiest fix is to match any command when you set up your substitute:
var result = CreateSuccessfulResult(); // <-- fill this in as appropriate
mockProcessor.Submit(null).ReturnsForAnyArgs(result);
Setting the Success field on result inline as per your original test should work too I think, but I find it a bit clearer to specify the result required.
You can improve this a bit by matching the expected command type:
mockProcessor.Submit(Arg.Any<CreateOrUpdateCompanyCommand>()).Returns(result);
You can also inspect the command passed if you'd like to test that:
mockProcessor
.Submit(Arg.Is<CreateOrUpdateCompanyCommand>(x => x.CompanyId = ...))
.Returns(result);
A similar approach can be used to check the Validate call.
There's a bit more info in the NSubstitute docs.
I am running on Castle's trunk, and trying to unit-test a controller-action where validation of my DTO is set up. The controller inherits from SmartDispatcherController. The action and DTO look like:
[AccessibleThrough(Verb.Post)]
public void Register([DataBind(KeyReg, Validate = true)] UserRegisterDto dto)
{
CancelView();
if (HasValidationError(dto))
{
Flash[KeyReg] = dto;
Errors = GetErrorSummary(dto);
RedirectToAction(KeyIndex);
}
else
{
var user = new User { Email = dto.Email };
// TODO: Need to associate User with an Owning Account
membership.AddUser(user, dto.Password);
RedirectToAction(KeyIndex);
}
}
public class UserRegisterDto
{
[ValidateNonEmpty]
[ValidateLength(1, 100)]
[ValidateEmail]
public string Email { get; set; }
[ValidateSameAs("Email")]
public string EmailConfirm { get; set; }
[ValidateNonEmpty]
public string Password { get; set; }
[ValidateSameAs("Password")]
public string PasswordConfirm { get; set; }
// TODO: validate is not empty Guid
[ValidateNonEmpty]
public string OwningAccountIdString { get; set; }
public Guid OwningAccountId
{
get { return new Guid(OwningAccountIdString); }
}
[ValidateLength(0, 40)]
public string FirstName { get; set; }
[ValidateLength(0, 60)]
public string LastName { get; set; }
}
The unit test looks like:
[Fact]
public void Register_ShouldPreventInValidRequest()
{
PrepareController(home, ThorController.KeyPublic, ThorController.KeyHome, HomeController.KeyRegister);
var dto = new UserRegisterDto { Email = "ff" };
home.Register(dto);
Assert.True(Response.WasRedirected);
Assert.Contains("/public/home/index", Response.RedirectedTo);
Assert.NotNull(home.Errors);
}
("home" is my HomeController instance in the test; home.Errors holds a reference to an ErrorSummary which should be put into the Flash when there's a validation error).
I am seeing the debugger think that dto has no validation error; it clearly should have several failures, the way the test runs.
I have read Joey's blog post on this, but it looks like the Castle trunk has moved on since this was written. Can someone shed some light, please?
http://www.candland.net/blog/2008/07/09/WhatsNeededForCastleValidationToWork.aspx would appear to contain an answer.