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: }