Tuesday, February 23, 2016

Unit Testing Asp.net MVC View Model Validation

ViewModel attribute validation is a key feature of ASP.NET MVC, but is often not a tested one.  For the most part developers verify controller actions, pass in a model, mock the ModelState.IsValid property and call it good, never checking to make sure if the model is validating correctly.  One of the biggest reasons is developers just don’t know how, it’s not difficult, but it’s not very intuitive so here is a quick tutorial.

First we take a view model with some basic validation attributes

   1:  public class RegisterViewModel
   2:  {
   3:      [Required]
   4:      [EmailAddress]
   5:      [Display(Name = "Email")]
   6:      public string Email { get; set; }
   7:   
   8:      [Required]
   9:      [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
  10:      [DataType(DataType.Password)]
  11:      [Display(Name = "Password")]
  12:      public string Password { get; set; }
  13:   
  14:      [DataType(DataType.Password)]
  15:      [Display(Name = "Confirm password")]
  16:      [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
  17:      public string ConfirmPassword { get; set; }
  18:  }

Next we create a simple reusable testing context class

   1:  public abstract class ViewModelValidation_Context<T> where T : new()
   2:  {
   3:      public T Target;
   4:      public List<ValidationResult> ActualMessages;
   5:      public bool Actual;
   6:   
   7:      [SetUp]
   8:      public void SetUp()
   9:      {
  10:          Context();
  11:          Because();
  12:      }
  13:   
  14:      public virtual void Context()
  15:      {
  16:          Target = new T();
  17:          ActualMessages = new List<ValidationResult>();
  18:      }
  19:   
  20:      public virtual void Because()
  21:      {
  22:          var context = new ValidationContext(Target, null, null);
  23:          Actual = Validator.TryValidateObject(Target, context, ActualMessages, true);
  24:      }
  25:  }

Notice this test context class is a little different than what I have used in the past, we are predefining the Because() method.  In this case regardless of the class we are testing we are still going to run what is really the meat of this class, we create a Validation Context and then passes it to System.ComponentModel.DataAnnotations.Validator.TryValidateObject and this returns if the view model passed validation and and validation messages.

Next lets put this into action with a test to validate our model

   1:  [TestFixture]
   2:  public class When_Testing_RegisterViewModel_Validation_Test : ViewModelValidation_Context<RegisterViewModel>
   3:  {
   4:      public override void Context()
   5:      {
   6:          base.Context();
   7:          Target.Email = "TestEmail@email.com";
   8:          Target.Password = "TestPass";
   9:          Target.ConfirmPassword = "TestPass";
  10:      }
  11:   
  12:      [Test]
  13:      public void Passed_Validation_Test()
  14:      {
  15:          Assert.IsTrue(Actual);
  16:      }
  17:   
  18:      [Test]
  19:      public void Has_No_ValidationMessages_Test()
  20:      {
  21:          Assert.IsFalse(ActualMessages.Any());
  22:      }
  23:  }

The model validation requires we have a valid email, we have a password, a confirm password, and password and confirm password match, in this case everything is good and we get a true for is valid and no validation massages.

Next lets test an invalid model

   1:  [TestFixture]
   2:  public class When_Testing_RegisterViewModel_Validation_With_NoPassword_Test : ViewModelValidation_Context<RegisterViewModel>
   3:  {
   4:      public override void Context()
   5:      {
   6:          base.Context();
   7:          Target.Email = "TestEmail@email.com";
   8:          Target.Password = string.Empty;
   9:          Target.ConfirmPassword = "TestPass";
  10:      }
  11:   
  12:      [Test]
  13:      public void Fail_Validation_Test()
  14:      {
  15:          Assert.IsFalse(Actual);
  16:      }
  17:   
  18:      [Test]
  19:      public void Has_No_ValidationMessages_Test()
  20:      {
  21:          Assert.IsTrue(ActualMessages.Any());
  22:      }
  23:   
  24:      [TestCase("The Password field is required.")]
  25:      [TestCase("The password and confirmation password do not match.")]
  26:      public void Has_Expected_Validation_ErrorMessages_Test(string message)
  27:      {
  28:          Assert.IsTrue(ActualMessages.Any(x=>x.ErrorMessage== message));
  29:      }
  30:   
  31:      [TestCase("Password")]
  32:      public void Has_Expected__ValidationMessages_Test(string memberName)
  33:      {
  34:          Assert.IsTrue(ActualMessages.Any(x => x.MemberNames.Contains(memberName)));
  35:      }
  36:  }

In this test we made password an empty string and as a result it failed validation, and returned a validation message for password being required and for password and confirm password not matching.

Check out my sample application on Github to see this in action.

Thursday, February 18, 2016

I agree With Apple and San Bernardino County is at fault

There is currently a court case in San Bernardino where the FBI is attempting to compel Apple to build an update to IOS that would allow them to access encrypted data on the work phone of Syed Rizwan Farook (Farook and his wife Tashfeen Malik were responsible, authorities say, for killing 14 people and injuring another 22 who were attending a training event and holiday party last December 2 ). 

The FBI has requested and has been granted by Magistrate Sheri Pym, in the US District Court of Central California, a court order to force Apple to provide the FBI with software to defeat a self-destruct mechanism on the iPhone. Under the pretense that the phone belongs to the county and the county has the right to access any and all information on their device. To this Tim Cook (CEO of Apple) responded with an open letter to their customers saying they will not comply with the court order.  Apple is not disobeying the court order because it supports murdering terrorists, but because they value the rights of their users and are not going to deliberately circumvent their security. 

In a statements to Fox News one of the against working the San Bernardino case as stated “Nobody can build a phone that we cannot get in under unique circumstances. Why should Apple be allowed to build a phone that does that?” and  “The right should not supersede our ability to keep people safe. It’s why we are not finding others, encryption, and, specifically in this case, we cannot connect the dots.” source.  Here is where the DOJ, the FBI and the Judge is wrong and Apple is right.  “Our Rights” to not self-incriminate do supersede, and are guaranteed by the constitution.  To make this a little more clear the court order is basically the same as getting a court order to force MasterLock to update all of their padlocks to use a master key so the government can open them if they need to. 

Regardless of who owns what is being locked, by forcing a company to add security vulnerabilities to their products is wrong, reckless, and fundamentally against everything our country was founded on.  I understand the DOJ’s position but they are wrong. 

Why San Bernardino is at fault, if you are going to issue devices as a company/government entity (this was a work phone) you should be managing your hardware to protect your assets, this is the basic bus factor.  If an employee has a company device, the company owns it, and everything on it.  Outside of this case what happens if an employee has valuable company information on a device and the person is in a car wreak?  Digital assets, like all assets, need to be managed correctly, the device manufacture should not be forced to weaken their product for your bad planning.