Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Monday, March 26, 2018

Boise Code Camp Presentation on Into to Continues Integration

This year at Boise Code Camp I presented on Into to Continues Integration and here are the Slides and the source Code https://github.com/bobtjanitor/SampleTDDApp

Slides from my Into to TDD talk at Boise Code camp

This year at Boise Code Camp I presented on Into to TDD and here are the slides and the source Code https://github.com/bobtjanitor/SampleTDDApp

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, November 28, 2013

Testing un-mockable base class methods

Working on a project using the Xamarin Framework and came across a problem where I needed to see if a method was being called.  The problem was the method was on the base class and was un-mockable

To get around this I used a lazy load property and a delegate to create a wrapper.
   1: private StartActivityDelegate _start;
   2: public StartActivityDelegate Start
   3: {
   4:     get { return _start ?? (_start = StartActivity); }
   5:     set { _start = value; }
   6: }
   7:  
   8: public delegate void StartActivityDelegate (Type type);
   9:  
  10: protected override void OnCreate(Bundle bundle)
  11: {
  12:     base.OnCreate(bundle);
  13:     Redirect();
  14: }
  15:  
  16: public void Redirect()
  17: {
  18:     if (Preferences.HasRequired())
  19:     {
  20:         Start(typeof(ClientActivity));
  21:     }
  22:     else
  23:     {
  24:         Start(typeof(SettingsActivity));
  25:     }   
  26: }
To test it you simply set the property in my test class to a test method
   1: [TestFixture]
   2: public class When_Starting_SplashScreen_Without_Required_Preferences_Test
   3: {
   4:     public SplashActivity Target;
   5:     public Type Actual;
   6:     public PreferencesMock PreferencesMock;
   7:  
   8:     public void TestStartActivity(Type type)
   9:     {
  10:         Actual = type;
  11:     }
  12:  
  13:     [SetUp]
  14:     public void SetUp()
  15:     {
  16:         PreferencesMock = new PreferencesMock { HasRequiredStub = false };
  17:  
  18:         Target = new SplashActivity { Start = TestStartActivity, Preferences = PreferencesMock };
  19:         Target.Redirect();
  20:     }
  21:  
  22:     [Test]
  23:     public void Has_Expected_Activity_Test()
  24:     {
  25:         Assert.AreEqual(typeof(SettingsActivity), Actual);
  26:     }
  27: }
Next to test that we are calling the base class method we are expecting, we add a new test fixture and use a little reflection to get the method info for each and then compare them
   1: [TestFixture]
   2: public class SplashActivity_Tests
   3: {
   4:     public SplashActivity Target;
   5:     
   6:     [SetUp]
   7:     public void SetUp()
   8:     {
   9:         Target = new SplashActivity();            
  10:     }
  11:  
  12:     [Test]
  13:     public void LazyLoads_Write_Test()
  14:     {
  15:         MethodInfo actual = Target.Start.GetMethodInfo();
  16:         MethodInfo expected = typeof(Activity).GetMethod("StartActivity");
  17:         Assert.AreSame(expected, actual);
  18:     }      
  19: }

Tuesday, January 8, 2013

Tools only give you the ability

Recently I read a post on the Alt.net linked-in group Would mocking help prevent these software bugs? talking about the Knight Capital Group Inc.’s disaster, asking the question "if doing more testing and using mocks would have prevented it?"

Doing unit testing with mocking is a tool, not a silver bullet. Unit tests mixed with behavior testing can be very effective at finding these kinds of bugs and Mocks allow you to create and test against foreseen conditions.  While TDD can help expose bugs, unforeseen data conditions can pass though unnoticed, tests only test what they are told to.

Something else to keep in mind is behavior and unit tests are only a small part of the testing tool box that also includes: integration tests, performance tests, automated UI tests, etc.  and that’s what these are tools.  How effective they are depends on the user, a funny quote I once heard is “A tool runs tools, a craftsman uses them”.  You can run your code coverage tool all day long and say “look I have 80% code coverage” but this doesn't mean your code is well tested, it just means it has test that run though it, and can hide untested code. 

One commenter on the article said something to the effect that what they where working on requires to much performance to be testable.   I would like to state this is B.S. you can make testable code that has just as much performance as not testable code. You may have to change how you test the code, or use different design or testing techniques for example Dependency Injection isn't required to make testable code, if it's not fast enough use greedy constructors or lazy load properties. If you need to make a black box class for performance, you simply do integration tests around it.

In the end, saying TDD isn’t of value because it didn’t catch bug xyz is like a carpenter blaming his hammer for hitting his thumb and not the nail.  On the other side it’s just as ridiculous to say “I can build a house” simply because you own a hammer and some nails.

Wednesday, June 27, 2012

Running your test while you write them using NCrunch

A coworker told me about watching a presentation where the presenter used NCrunch, so I decided to take a look at it. 

The basic idea is NCrunch  automatically runs your tests in a parallel process giving you continuous testing that’s integrated into Visual Studio. It intelligently runs tests automatically so that you don't have to, and gives you useful information including code coverage and performance metrics while you work.

Here is an example of the inline test results

nCrunch_Code

here is a screenshot of the test runner

nCrunch_TestRunner

and this what the coverage looks like

nCrunch_CodeCoverage
it’s not quite as detailed as I would like, but not bad.

This isn’t going to be a replacement for my copy of Resharper and DotCover but at the same time, I don’t see a problem with running both.  This could defiantly save me a lot of time waiting for test to run and if I want more detailed information I can still use dotCover.

This is a free tool that so far has really impressed me, for more information go to the NCrunch home page athttp://www.ncrunch.net/

Monday, June 25, 2012

Testing for execution sequence

I ran into a problem the other day where I was doing some cost calculations and wasn’t getting the expected result, the problem turned out to be I was doing the calculations before everything got loaded, simple mistake.

Thought it would be interesting to show the solution I used for testing, lets say we have a simple method for updating an employee record.
   1: public bool UpdateEmployee(EmployeeDto updateEmployee)
   2: {
   3:     var result = false;
   4:     if(AuthRequests.UserCanUpdateEmployee(User.Id,updateEmployee.Id))
   5:     {
   6:         result = EmployeeRepository.UpdateEmployee(updateEmployee);
   7:     }
   8:     return result;
   9: }
fairly simple, you call AuthRequests to see if the user and update the employee and then you update the employee.  Here is an example test class for it
   1: [TestFixture]
   2: public class When_Updating_Employee_Information_Test: Test_Context<EmployeeRequests>
   3: {
   4:     public Mock<IAuthRequests> AuthRequestsMock;
   5:     public bool ExpectedAuth;
   6:     public int UserId;
   7:     public int EmployeeId;
   8:     public bool Actual;
   9:     public bool Expected;
  10:     public EmployeeDto UpdateEmployee;
  11:     public EmployeeDto SentEmployee;
  12:     public UserDto ExpectedUser;
  13:     public Mock<IEmployeeRepository> MockEmployeeRepository;
  14:  
  15:     public override void Context()
  16:     {
  17:         base.Context();
  18:         UserId = 12;
  19:         EmployeeId = 14;
  20:         Expected = true;
  21:         ExpectedAuth = true;
  22:         UpdateEmployee = new EmployeeDto{Id = EmployeeId};
  23:         Target.User.Id = UserId;
  24:         AuthRequestsMock = new Mock<IAuthRequests>();
  25:         MockEmployeeRepository = new Mock<IEmployeeRepository>();
  26:         
  27:         AuthRequestsMock.Setup(x => x.UserCanUpdateEmployee(UserId, EmployeeId)).Returns(ExpectedAuth);
  28:         MockEmployeeRepository.Setup(x => x.UpdateEmployee(It.IsAny<EmployeeDto>())).Callback<EmployeeDto>(
  29:             x => SentEmployee = x).Returns(Expected);
  30:  
  31:         Target.AuthRequests = AuthRequestsMock.Object;
  32:         Target.EmployeeRepository = MockEmployeeRepository.Object;
  33:     }
  34:  
  35:     public override void Because()
  36:     {
  37:         Actual = Target.UpdateEmployee(UpdateEmployee);
  38:     }
  39:  
  40:     [Test]
  41:     public void Calls_AuthRequests_UserCanUpdateEmployee_Test()
  42:     {
  43:         AuthRequestsMock.Verify(x=>x.UserCanUpdateEmployee(UserId, EmployeeId), Times.Once());
  44:     } 
  45:  
  46:     [Test]
  47:     public void Calls_EmployeeRepository_UpdateEmployee_Test()
  48:     {
  49:         MockEmployeeRepository.Verify(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()), Times.Once());
  50:     }
  51:  
  52:     [Test]
  53:     public  void Sends_expected_Emplyee_test()
  54:     {
  55:         Assert.AreEqual(UpdateEmployee.Id, SentEmployee.Id);
  56:     }
  57:  
  58:     [Test]
  59:     public void Returns_Expected_Result_Test()
  60:     {
  61:         Assert.IsTrue(Actual);
  62:     }
  63: }
the only problem is the tests still pass if the method looks like this
   1: public bool UpdateEmployee(EmployeeDto updateEmployee)
   2: {
   3:     var result = EmployeeRepository.UpdateEmployee(updateEmployee);
   4:     AuthRequests.UserCanUpdateEmployee(User.Id,updateEmployee.Id);
   5:     return result;
   6: }
so lets add some sequence tests, basically we add a counter and a dictionary<string,int> to record what was executed and it what order using the Moq callback.
   1: [TestFixture]
   2: public class When_Updating_Employee_Information_With_sequence_Test : Test_Context<EmployeeRequests>
   3: {
   4:     public Mock<IAuthRequests> AuthRequestsMock;
   5:     public bool ExpectedAuth;
   6:     public int UserId;
   7:     public int EmployeeId;
   8:     public bool Actual;
   9:     public bool Expected;
  10:     public EmployeeDto UpdateEmployee;
  11:     public EmployeeDto SentEmployee;
  12:     public UserDto ExpectedUser;
  13:     public Mock<IEmployeeRepository> MockEmployeeRepository;
  14:     public int Counter;
  15:     public Dictionary<string, int> Sequence; 
  16:  
  17:     public override void Context()
  18:     {
  19:         base.Context();
  20:         UserId = 12;
  21:         EmployeeId = 14;
  22:         Expected = true;
  23:         ExpectedAuth = true;
  24:         UpdateEmployee = new EmployeeDto { Id = EmployeeId };
  25:         Counter = 0;
  26:         Sequence = new Dictionary<string, int>();
  27:         Target.User.Id = UserId;
  28:         AuthRequestsMock = new Mock<IAuthRequests>();
  29:         MockEmployeeRepository = new Mock<IEmployeeRepository>();
  30:  
  31:         AuthRequestsMock.Setup(x => x.UserCanUpdateEmployee(UserId, EmployeeId))
  32:             .Callback(() => Sequence.Add("AuthRequests.UserCanUpdateEmployee", Counter++)).Returns(ExpectedAuth);
  33:         MockEmployeeRepository.Setup(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()))
  34:             .Callback<EmployeeDto>(x =>
  35:                  {
  36:                     SentEmployee = x;
  37:                     Sequence.Add("EmployeeRepository.UpdateEmployee", Counter++);
  38: }).Returns(Expected);
  39:  
  40:         Target.AuthRequests = AuthRequestsMock.Object;
  41:         Target.EmployeeRepository = MockEmployeeRepository.Object;
  42:     }
  43:  
  44:     public override void Because()
  45:     {
  46:         Actual = Target.UpdateEmployee(UpdateEmployee);
  47:     }
  48:  
  49:     [Test]
  50:     public void Calls_AuthRequests_UserCanUpdateEmployee_Test()
  51:     {
  52:         AuthRequestsMock.Verify(x => x.UserCanUpdateEmployee(UserId, EmployeeId), Times.Once());
  53:     }
  54:  
  55:     [Test]
  56:     public void Calls_AuthRequests_UserCanUpdateEmployee_Inorder_Test()
  57:     {
  58:         Assert.AreEqual(0, Sequence["AuthRequests.UserCanUpdateEmployee"]);
  59:     }
  60:  
  61:     [Test]
  62:     public void Calls_EmployeeRepository_UpdateEmployee_Test()
  63:     {
  64:         MockEmployeeRepository.Verify(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()), Times.Once());
  65:     }
  66:  
  67:     [Test]
  68:     public void Calls_EmployeeRepository_UpdateEmployee_Inorder_Test()
  69:     {
  70:         Assert.AreEqual(1, Sequence["EmployeeRepository.UpdateEmployee"]);
  71:     }
  72:  
  73:     [Test]
  74:     public void Sends_expected_Emplyee_test()
  75:     {
  76:         Assert.AreEqual(UpdateEmployee.Id, SentEmployee.Id);
  77:     }
  78:  
  79:     [Test]
  80:     public void Returns_Expected_Result_Test()
  81:     {
  82:         Assert.IsTrue(Actual);
  83:     }
  84: }
for the full source see the sample application here.