I am trying to create unit tests for my state machine to check if each state does what it is supposed to do.
I can bring the state machine into any state by publishing the event that starts the state machine and creating all the variables that I need to reach the desired state.
Simplified state machine:
public class CheckFilesStateMachine : MassTransitStateMachine<DataState>, ICheckFilesStateMachine
{
#region Events
public Event<IEventCheckFile> CheckFile { get; private set; }
#endregion
#region States
public State CheckingForFiles { get; }
public State ValidatingFiles { get; }
public State NoFilesFound { get; }
public State Validated { get; }
public State FilesProcessed { get; }
public State ValidationFailed{ get; }
#endregion
public DataStateMachine(IDataService dataService)
{
InstanceState(x => x.CurrentState);
Event(() => CheckFile, x => x.CorrelateById(context => context.Message.Id));
Initially(When(CheckFile).TransitionTo(CheckingForFiles));
WhenEnter(CheckingForFiles, binder => binder
.Then(x => x.Instance.Files= dataService.GetFiles())
.IfElse(x => dataService.HasFiles(),
x => x.TransitionTo(ValidatingFiles),
x => x.TransitionTo(NoFilesFound))
.Catch<Exception>(ex => ex
.Then(x => Console.WriteLine(x.Exception.Message))));
WhenEnter(ValidatingFiles, binder => binder
.IfElse(x => dataService.ValidateFiles(),
x => x.TransitionTo(Validated),
x => x.TransitionTo(ValidationFailed))
.Catch<Exception>(ex => ex
.Then(x => Console.WriteLine(x.Exception.Message))));
WhenEnter(NoFilesFound, binder => binder.Then(x => Console.WriteLine("No Files found")).Finalize());
WhenEnter(Validated, binder => binder.Then(x => Console.WriteLine("Validated")).Finalize());
WhenEnter(ValidationFailed, binder => binder.Then(x => Console.WriteLine("Validation Failed")).Finalize());
SetCompletedWhenFinalized();
}
}
I wrote some unit tests and I can start the machine (followed this guide from the MassTransit Documentation), but I want to check if the states CheckingForFiles and ValidatingFiles are doing what they are supposed to do independent of each other.
So far, I can mock the IDataService methods to reach the desired state by following the flow of the state machine, but is there a way to start the state machine and just jump to a specific state?
UPDATE
Let's say I have another state in there:
public class CheckFilesStateMachine : MassTransitStateMachine<DataState>, ICheckFilesStateMachine
{
#region Events
public Event<IEventCheckFile> CheckFile { get; private set; }
#endregion
#region States
public State CheckingForFiles { get; }
public State ValidatingFiles { get; }
public State DeletingFiles{ get; }
public State NoFilesFound { get; }
public State Validated { get; }
public State FilesProcessed { get; }
public State ValidationFailed{ get; }
#endregion
public DataStateMachine(IDataService dataService)
{
InstanceState(x => x.CurrentState);
Event(() => CheckFile, x => x.CorrelateById(context => context.Message.Id));
Initially(When(CheckFile).TransitionTo(CheckingForFiles));
WhenEnter(CheckingForFiles, binder => binder
.Then(x => x.Instance.Files= dataService.GetFiles())
.IfElse(x => dataService.HasFiles(),
x => x.TransitionTo(ValidatingFiles),
x => x.TransitionTo(NoFilesFound))
.Catch<Exception>(ex => ex
.Then(x => Console.WriteLine(x.Exception.Message))));
WhenEnter(ValidatingFiles, binder => binder
.IfElse(x => dataService.ValidateFiles(),
x => x.TransitionTo(Validated),
x => x.TransitionTo(DeletingFiles))
.Catch<Exception>(ex => ex
.Then(x => Console.WriteLine(x.Exception.Message))));
WhenEnter(DeletingFiles, binder => binder
.Then(x => x.Instance.Files= dataService.RemoveFiles())
.TransitionTo(ValidationFailed)
.Catch<Exception>(ex => ex
.Then(x => Console.WriteLine(x.Exception.Message))));
WhenEnter(NoFilesFound, binder => binder.Then(x => Console.WriteLine("No Files found")).Finalize());
WhenEnter(Validated, binder => binder.Then(x => Console.WriteLine("Validated")).Finalize());
WhenEnter(ValidationFailed, binder => binder.Then(x => Console.WriteLine("Validation Failed")).Finalize());
SetCompletedWhenFinalized();
}
}
In this case, can I enter in CheckingForFiles and then jump to DeletingFiles? Is it possible?
If you are using the container-based in-memory test harness, you can resolve the saga dictionary from the container and add an instance:
var dictionary = provider.GetRequiredService<IndexedSagaDictionary<DataState>>();
dictionary.Add(new SagaInstance<DataState>(new DataState()
{
CorrelationId = dataId,
CurrentState = "CheckingForFiles"
}));
The latest version of MassTransit, when using the test harness, has an extension method to add a saga instance:
harness.AddSagaInstance<DataState>(dataId, x =>
{
x.CurrentState = "CheckingForFiles";
});