Polymorphic test setup with template method

We had a kind of duplication in our tests that we didn’t know how to deal with. The refactoring “Introduce Polymorphic Creation with Factory Method” explained by Joshua Kerievsky in his brilliant book “Refactoring to Patterns” gave me the solution to avoid duplicated tests.

[TestFixture] public class
 ChangingColorWithImplicitExclusionsShould : ConfigurationTests {
   [Test] public void 
not_allow_change_when_its_compulsory_has_the_same_family_than_a_configured_equipment() {
      CatalogBuilder
        .AddEquipmentWithFamily("PR1", "Radio")
        .AddEquipmentWithFamily("PR2", "Radio")
        .AddColor("red", WithEquipmentsCompulsory("PR2")); 
      var configuration = Agiven.ModelConfiguration()
         .With(Agiven.ConfigEquipment("PR1"))
         .Build();

       Expect.CallTo(() => ExecuteChangeColor("red", configuration))
            .ToThrow();
  }
}

[TestFixture] public class
 ChangingInteriorWithImplicitExclusionsShould : ConfigurationTests {
   [Test] public void 
not_allow_change_when_its_compulsory_has_the_same_family_than_a_configured_equipment() {
      CatalogBuilder
        .AddEquipmentWithFamily("PR1", "Radio")
        .AddEquipmentWithFamily("PR2", "Radio")
        .AddInterior("xyz", WithEquipmentsCompulsory("PR2")); 
      var configuration = Agiven.ModelConfiguration()
         .With(Agiven.ConfigEquipment("PR1"))
         .Build();

       Expect.CallTo(() => ExecuteChangeInterior("xyz", configuration))
            .ToThrow();
 }
}

Tests are very similar, the differences are in lines 8 and 25, and also in lines 13 and 30. First tests tries to change the color of a configuration whereas the second one tries the interior. Part of the handling business logic is the same. This is just one scenario but we had many of them, with same expected behavior for color, interior, equipment, and more. Eventually there was a lot of “duplication”.

After refactoring, we have a base abstract class with the tests, exposing template methods that child classes have to implement in order to populate the catalog and also to execute the corresponding action:

[TestFixture] public abstract class 
CantChangeConfigurationBecauseThereisImplicitExclusionWhen : ConfigurationTests {
    [Test] public void
its_compulsory_has_the_same_family_than_a_configured_equipment() {
      CatalogBuilder
          .AddEquipmentWithFamily("PR1", "Radio")
          .AddEquipmentWithFamily("PR2", "Radio");
      AddImplicitExclusionItemWithCompulsories("PR2");
      var configuration = Agiven.ModelConfiguration()
          .With(Agiven.ConfigEquipment("PR1"))
          .Build();

       Expect.CallTo(ChooseConflictiveItem(configuration))
          .ToThrow();
  }

  protected abstract void AddImplicitExclusionItem(string code);
  protected abstract Func ChooseConflictiveItem(ModelConfiguration configuration);
}

[TestFixture] public class 
ChangingColor : CantChangeConfigurationBecauseThereisImplicitExclusionWhen 
   private const string itemCode = "irrelevant";

   protected override void AddImplicitExclusionItem(string code){
         CatalogBuilder
            .AddColor(itemCode, WithEquipmentsCompulsory(code));
   }

   protected override Func ChooseConflictiveItem(ModelConfiguration configuration){
            return () => ExecuteChangeColor(itemCode, configuration);
        }
}       

The base class “ConfigurationTests” contains just helper methods such as ExecuteChangeColor, or ExecuteChangeInterior, but no tests at all. Otherwise tests would run twice.