title: XUnit - design architecture for placing reusable tests helpers in an external shared class library with Extension Methods
body: <p>Designing some XUnit tests for a very large number of business object classes (over 400). There will be a lot of repeated code so employing DRY principles we have a number of options of how to abstract and encapsulate code into shared class libraries.</p>
  
  <ol>
  <li>Traditional static helper class. A method for common tests, passing in parameters for the instance and any settings.</li>
  <li>Extension Methods on the business class. As above but as an extension method on the business class/interface type itself.</li>
  </ol>
  
  <p><strong>Creating tests as Extension Methods (option 2), does this break the accepted principle of Unit Testing which prompts to test only small specific unit. As now chains of small tests can be grouped into a parent test?</strong> </p>
  
  <p><strong>Are there other design architecture patterns which enable reuse of shared libraries for common tests to domain entities?</strong></p>
  
  <p>The code below demonstrates the two options. Please note the Helper classes will be in another Class Lib project separate from the UnitTest class (Test_Person) project. </p>
  
  <pre><code>using Xunit;
  namespace CCTools.UnitTest.Main.XUnit.UnitTestPatterns
  {
  
  public class Test_Person
  {
      /// &lt;summary&gt;
      ///     Use Extension methods, quick to code and easy to read.
      /// &lt;/summary&gt;
      [Fact]
      public static void Test_Stuff_UseExtenions_A()
      {
          Person p = new Person();
          //Do lots of other config and tests...
          p.Test_IncreaseCreditRating(187);
          p.IsManager = true;
          p.Test_CreditRating_is_valid_for_manager();
      }
  
      [Fact]
      public static void Test_Stuff_UseExtenions_B()
      {
          Person p = new Person();
          //Do lots of other config and tests...
  
          p.Test_IncreaseCreditRating(59);
          p.Test_CreditRating_is_valid_for_manager();
      }
  
      /// &lt;summary&gt;
      ///     Use traditional Helper methods, not as easy to read or code.
      /// &lt;/summary&gt;
      [Fact]
      public static void Test_Stuff_UseHelper_A()
      {
          Person p = new Person();
          //Do lots of other config and tests...
  
          PersonUniTestHelper.Test_IncreaseCreditRating(p, 10);
          p.IsManager = true;
          PersonUniTestHelper.Test_CreditRating_is_valid_for_manager(p);
      }
  }
  /// &lt;summary&gt;
  ///     Extension methods, which makes coding far easier on the eye and one is able to locate all methods
  ///     using intellisense at a glance.
  /// &lt;/summary&gt;
  public static class PersonUniTestExtensionMethods
  {
      public static void Test_IncreaseCreditRating(this Person person, int creditRating)
      {
          person.IncreaseCreditRating(creditRating);
          if (creditRating &gt;= 100) Assert.Equal(100 * 1000, person.CreditLimit);
          else Assert.Equal(creditRating * 1000, person.CreditLimit);
      }
  
      public static void Test_CreditRating_is_valid_for_manager(this Person person)
      {
          if (person.IsManager) Assert.True(person.CreditRating &gt;= 50);
      }
  }
  
  /// &lt;summary&gt;
  ///     Traditional static class for helpers
  /// &lt;/summary&gt;
  public static class PersonUniTestHelper
  {
      public static void Test_IncreaseCreditRating(Person person, int creditRating)
      {
          person.IncreaseCreditRating(creditRating);
          if (creditRating &gt;= 100) Assert.Equal(100 * 1000, person.CreditLimit);
          else Assert.Equal(creditRating * 1000, person.CreditLimit);
      }
  
      public static void Test_CreditRating_is_valid_for_manager(Person person)
      {
          if (person.IsManager) Assert.True(person.CreditRating &gt;= 50);
      }
  }
  
  
  public class Person
  {
      public string FirstName { get; set; }
      public int CreditLimit { get; private set; }
      public int CreditRating { get; private set; }
  
      public bool IsManager { get; set; }
  
      /// &lt;summary&gt;
      ///     Simple action to set CreditLimit based on Rating.
      /// &lt;/summary&gt;
      public void IncreaseCreditRating(int creditRating)
      {
          CreditRating = creditRating;
          CreditLimit = creditRating &gt;= 100 ? 100 * 1000 : creditRating * 1000;
      }
  }
  }
  </code></pre>