title: How to successfully drive a MassTransitStateMachine via the InMemoryTestHarness?
body: <p>Following up to: <a href="https://stackoverflow.com/questions/49763600/how-to-write-masstransitstatemachine-unit-tests">How to write MassTransitStateMachine unit tests?</a></p>
  
  <p>Here's a simple test class (using MS Test) for a simple state machine called <code>ProcedureStateMachine</code> (note: this is not a real production state machine for us... just an experiment I'd used to play around with <code>MassTransitStateMachine</code> a while back..  it seemed a handy self-contained place to experiment with getting unit testing going too):</p>
  
  <pre><code>[TestClass]
  public class ProcedureStateMachineTests
  {
      private ProcedureStateMachine _machine;
      private InMemoryTestHarness _harness;
      private StateMachineSagaTestHarness&lt;ProcedureContext, ProcedureStateMachine&gt; _saga;
  
      [TestInitialize]
      public void SetUp()
      {
          _machine = new ProcedureStateMachine();
          _harness = new InMemoryTestHarness();
          _saga = _harness.StateMachineSaga&lt;ProcedureContext, ProcedureStateMachine&gt;(_machine);
  
          _harness.Start().Wait();
      }
  
      [TestCleanup]
      public void TearDown()
      {
          _harness.Stop().Wait();
      }
  
      [TestMethod]
      public async Task That_Can_Start()
      {
          // Arrange
          // Act
          await _harness.InputQueueSendEndpoint.Send(new BeginProcessing
          {
              ProcedureId = Guid.NewGuid(),
              Steps = new List&lt;string&gt; {"A", "B", "C" }
          });
  
          // Assert
          var sagaContext = _saga.Created.First();
          sagaContext.Saga.RemainingSteps.ShouldHaveCountOf(2);
      }
  }
  </code></pre>
  
  <p>And here's the state machine class itself:</p>
  
  <pre><code>public class ProcedureStateMachine : MassTransitStateMachine&lt;ProcedureContext&gt;
  {
      public State Processing { get; private set; }
      public State Cancelling { get; private set; }
      public State CompleteOk { get; private set; }
      public State CompleteError { get; private set; }
      public State CompleteCancelled { get; private set; }
  
      public Event&lt;BeginProcessing&gt; Begin { get; private set; }
      public Event&lt;StepCompleted&gt; StepDone { get; private set; }
      public Event&lt;CancelProcessing&gt; Cancel { get; private set; }
      public Event&lt;FinalizeProcessing&gt; Finalize { get; private set; }
  
      public ProcedureStateMachine()
      {
          InstanceState(x =&gt; x.CurrentState);
  
          Event(() =&gt; Begin);
          Event(() =&gt; StepDone);
          Event(() =&gt; Cancel);
          Event(() =&gt; Finalize);
  
          BeforeEnterAny(binder =&gt; binder
              .ThenAsync(context =&gt; Console.Out.WriteLineAsync(
                  $"ENTERING STATE [{context.Data.Name}]")));
  
          Initially(
              When(Begin)
                  .Then(context =&gt;
                  {
                      context.Instance.RemainingSteps = new Queue&lt;string&gt;(context.Data.Steps);
                  })
                  .ThenAsync(context =&gt; Console.Out.WriteLineAsync(
                      $"EVENT [{nameof(Begin)}]: Procedure [{context.Data.ProcedureId}] Steps [{string.Join(",", context.Data.Steps)}]"))
                  .Publish(context =&gt; new ExecuteStep
                  {
                      ProcedureId = context.Instance.CorrelationId,
                      StepId = context.Instance.RemainingSteps.Dequeue()
                  })
                  .Publish(context =&gt; new SomeFunMessage
                  {
                      CorrelationId = context.Data.CorrelationId,
                      TheMessage = $"Procedure [{context.Data.CorrelationId} has begun..."
                  })
                  .TransitionTo(Processing)
              );
  
          During(Processing,
              When(StepDone)
                  .Then(context =&gt;
                  {
                      if (null == context.Instance.AccumulatedResults)
                      {
                          context.Instance.AccumulatedResults = new List&lt;StepResult&gt;();
                      }
                      context.Instance.AccumulatedResults.Add(
                          new StepResult
                          {
                              CorrelationId = context.Instance.CorrelationId,
                              StepId = context.Data.StepId,
                              WhatHappened = context.Data.WhatHappened
                          });
                  })
                  .ThenAsync(context =&gt; Console.Out.WriteLineAsync(
                      $"EVENT [{nameof(StepDone)}]: Procedure [{context.Data.ProcedureId}] Step [{context.Data.StepId}] Result [{context.Data.WhatHappened}] RemainingSteps [{string.Join(",", context.Instance.RemainingSteps)}]"))
                  .If(context =&gt; !context.Instance.RemainingSteps.Any(),
                      binder =&gt; binder.TransitionTo(CompleteOk))
                  .If(context =&gt; context.Instance.RemainingSteps.Any(),
                      binder =&gt; binder.Publish(context =&gt; new ExecuteStep
                      {
                          ProcedureId = context.Instance.CorrelationId,
                          StepId = context.Instance.RemainingSteps.Dequeue()
                      })),
              When(Cancel)
                  .Then(context =&gt;
                  {
                      context.Instance.RemainingSteps.Clear();
                  })
                  .ThenAsync(context =&gt; Console.Out.WriteLineAsync(
                      $"EVENT [{nameof(Cancel)}]: Procedure [{context.Data.ProcedureId}] will be cancelled with following steps remaining [{string.Join(",", context.Instance.RemainingSteps)}]"))
                  .TransitionTo(Cancelling)
              );
  
          During(Cancelling,
              When(StepDone)
                  .Then(context =&gt;
                  {
                      context.Instance.SomeStringValue = "Booo... we cancelled...";
                  })
                  .ThenAsync(context =&gt; Console.Out.WriteLineAsync(
                      $"EVENT [{nameof(StepDone)}]: Procedure [{context.Data.ProcedureId}] Step [{context.Data.StepId}] completed while cancelling."))
                  .TransitionTo(CompleteCancelled));
  
          During(CompleteOk, When(Finalize).Finalize());
          During(CompleteCancelled, When(Finalize).Finalize());
          During(CompleteError, When(Finalize).Finalize());
  
          // The "SetCompleted*" thing is what triggers purging of the state context info from the store (eg. Redis)...  without that, the 
          // old completed state keys will gradually accumulate and dominate the Redis store.
          SetCompletedWhenFinalized();
      }
  }
  </code></pre>
  
  <p>When debug this test, the <code>_harness</code> has the <code>BeginProcessing</code> message in its <code>Sent</code> collection, but there's nothing in the <code>_saga.Created</code> collection. It seems like I'm missing some plumbing to cause the harness to actually drive the state machine when the messages are sent?</p>
  
  <p>====</p>
  
  <p>Removing the <code>.Wait()</code> calls from <code>SetUp()</code> and <code>TearDown()</code> and updating the test to the following does NOT change the behavior:</p>
  
  <pre><code>    [TestMethod]
      public async Task That_Can_Start()
      {
          try
          {
              await _harness.Start();
              // Arrange
  
              // Act
              await _harness.InputQueueSendEndpoint.Send(new BeginProcessing
              {
                  ProcedureId = Guid.NewGuid(),
                  Steps = new List&lt;string&gt; {"A", "B", "C"}
              });
  
              // Assert
              var sagaContext = _saga.Created.First();
              sagaContext.Saga.RemainingSteps.ShouldHaveCountOf(3);
          }
          finally
          {
              await _harness.Stop();
          }
      }
  </code></pre>