title: Approach to testing with FsCheck
body: <p>I am trying to make the paradigm shift to FsCheck and random property-based testing.  I have complex business workflows that have more test cases than I can possibly enumerate, and the business logic is a moving target with new features being added.</p>
  
  <p>Background: Match-making is a very common abstraction in Enterprise Resource Planning (ERP) systems.  Order fulfillment, supply chain logistics, etc.</p>
  
  <p>Example: Given a C and a P, determine if the two are a Match.  At any given point in time, some Ps are <em>never</em> Match-able, and some Cs are <em>never</em> Match-able.  Each has a Status that says whether they can be even considered for a Match.</p>
  
  <pre><code>public enum ObjectType {
    C = 0,
    P = 1
  }
  
  public enum CheckType {
    CertA = 0,
    CertB = 1
  }
  public class Check {
    public CheckType CheckType {get; set;}
    public ObjectType ObjectType {get; set;}
    /* If ObjectType == CrossReferenceObjectType, then it is assumed to be self-referential and there is no "matching" required. */
    public ObjectType CrossReferenceObjectType {get; set;}
    public int ObjectId {get; set;}
    public MatchStatus MustBeMetToAdvanceToStatus {get; set;}
    public bool IsMet {get; set;}
  }
  
  public class CStatus {
    public int Id {get; set;}
    public string Name {get; set;}
    public bool IsMatchable {get; set;}
  }
  
  public class C {
    public int Id {get; set;}
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public virtual CStatus Status {get;set;}
    public virtual IEnumerable&lt;Check&gt; Checks {get; set;}
    C() {
      this.Checks = new HashSet&lt;Check&gt;();
    }
  }
  
  public class PStatus {
    public int Id {get; set;}
    public string Name {get; set;}
    public bool IsMatchable {get; set;}
  }
  
  public class P {
    public int Id {get; set;}
    public string Title {get; set;}
    public virtual PStatus Status { get; set;}
    public virtual IEnumerable&lt;Check&gt; Checks {get; set;}
    P() {
      this.Checks = new HashSet&lt;Check&gt;();
    }
  }
  
  public enum MatchStatus {
    Initial = 0,
    Step2 = 1,
    Step3 = 2,
    Final = 3,
    Rejected = 4
  }
  
  public class Match {
    public int Id {get; set;}
    public MatchStatus Status {get; set;}
    public virtual C C {get; set;}
    public virtual P P {get; set;}
  }
  
  public class MatchCreationRequest {
    public C C {get; set;}
    public P P {get; set;}
  }
  
  public class MatchAdvanceRequest {
    public Match Match {get; set;}
    public MatchStatus StatusToAdvanceTo {get; set;}
  }
  
  public class Result&lt;TIn, TOut&gt; {
    public bool Successful {get; set;}
    public List&lt;string&gt; Messages {get; set;}
    public TIn InValue {get; set;}
    public TOut OutValue {get; set;}
    public static Result&lt;TIn, TOut&gt; Failed&lt;TIn&gt;(TIn value, string message)
    {
      return Result&lt;TIn, TOut&gt;() {
        InValue = value,
        Messages = new List&lt;string&gt;() { message },
        OutValue = null,
        Successful = false
      };
    }
    public Result&lt;TIn, TOut&gt; Succeeded&lt;TIn, TOut&gt;(TIn input, TOut output, string message)
    {
      return Result&lt;TIn, TOut&gt;() {
        InValue = input,
        Messages = new List&lt;string&gt;() { message },
        OutValue = output,
        Successful = true
      };
    }
  }
  
  public class MatchService {
     public Result&lt;MatchCreationRequest&gt; CreateMatch(MatchCreationRequest request) {
       if (!request.C.Status.IsMatchable) {
         return Result&lt;MatchCreationRequest, Match&gt;.Failed(request, "C is not matchable because of its status.");
       }
       else if (!request.P.Status.IsMatchable) {
         return Result&lt;MatchCreationRequest, Match&gt;.Failed(request, "P is not matchable because of its status.");
       }
       else if (request.C.Checks.Any(ccs =&gt; cs.ObjectType == ObjectType.C &amp;&amp; !ccs.IsMet) {
         return Result&lt;MatchCreationRequest, Match&gt;.Failed(request, "C is not matchable because its own Checks are not met.");
       } else if (request.P.Checks.Any(pcs =&gt; pcs.ObjectType == ObjectType.P &amp;&amp; !pcs.IsMet) {
         return Result&lt;MatchCreationRequest, Match&gt;.Failed(request, "P is not matchable because its own Checks are not met.");
       }
       else if (request.P.Checks.Any(pcs =&gt; pcs.ObjectType == ObjectType.C &amp;&amp; C.Checks.Any(ccs =&gt; !ccs.IsMet &amp;&amp; ccs.CheckType == pcs.CheckType))) {
         return Result&lt;MatchCreationRequest, Match&gt;.Failed(request, "P's Checks are not satisfied by C's Checks.");
       }
       else {
         var newMatch = new Match() { C = c, P = p, Status = MatchStatus.Initial }
         return Result&lt;MatchCreationRequest, Match&gt;.Succeeded(request, newMatch, "C and P passed all Checks.");
       }
     }
  }
  </code></pre>
  
  <p>Bonus: Beyond a naive "block Match" status, C and P each has a set of Checks.  Some Checks must be true for the C being Match-ed, some Checks must be true for the P being Match-ed, and some Checks for C must be cross-checked against the Checks for P.  This is where I suspect model-based testing with FsCheck will pay huge dividends, since (a) it is an example of a new feature added to the product (b) I can potentially write tests (user interactions) such as:</p>
  
  <ol>
  <li>Create</li>
  <li>After Create, Move forward through the pipeline</li>
  <li>Move backward (when is it allowed vs. not? ex: a Paid Order probably can't move back to a Purchase Approval step)</li>
  <li>Add/remove stuff (like Checks) while in the middle of pipeline</li>
  <li>If I ask to create a Match for the same C and P twice (e.g., concurrently with PLINQ), will I create duplicates?  (What message gets returned to the user?)</li>
  </ol>
  
  <p>Things I am struggling with:</p>
  
  <ol>
  <li>How should I generate test data for FsCheck?  I <em>think</em> the right way is to define all discrete possible combinations of Cs and Ps for creating a Match and have those be the "pre-conditions" for my model-based tests and the post-conditions be whether a Match should be created, but...</li>
  <li>Is that really the right approach? It feels a too deterministic for a <em>randomized</em> property-based testing tool. Is it over-engineering to even use FsCheck in such a situation?  Then, it's almost as if I have a data generator that ignores the seed value and returns a deterministic stream of test data.</li>
  <li>At this point, is FsCheck generators any different from just using xUnit.net and something like AutoPOCO?</li>
  </ol>