Hi there,
I’m building some workflows to drive the complex lifecycle of entities with lots of state and lots of signals coming from various external services. Unit tests for these workflows are getting real big real quick due to having to set expectations for the various involved Signals and Activities.
Thankfully the lifecycles they implement can be neatly sliced into phases. I was initially thinking of representing these slices as Child Workflows called by the main Workflow, which would provide a convenient abstraction for testing, but the docs advise against it.
My current approach is to implement these phases as methods similar to Workflow methods, and then wrap them in some interface which I can then easily mock.
Main workflow
// This is the mockable bit.
type PhaseRunner interface {
Execute(workflow.Context, PhaseInput) (PhaseOutput, error)
}
type Base struct {
phaseRunner PhaseRunner
}
func (base *Base) MyWorkflow(ctx workflow.Context) error {
output, err := base.phaseRunner.Execute(ctx, nil)
if err != nil {
return err
}
// do something with "output"
return nil
}
Phase implementation
func ExecutePhase(ctx workflow.Context, input PhaseInput) (output PhaseOutput, err error) {
// run activities, handle signals, etc...
// can be unit-tested like a Workflow
}
type PhaseRunnerFunc func(workflow.Context, PhaseInput) (PhaseOutput, error)
func (f *PhaseRunnerFunc) Execute(ctx workflow.Context, input PhaseInput) (output PhaseOutput, err error) {
return f(ctx, input)
}
Putting it all together
// Running the real thing.
func run(theWorker worker.Worker) error {
base := &Base{
phaseRunner: PhaseRunnerFunc(ExecutePhase),
}
theWorker.RegisterWorkflow(base.MyWorkflow)
return theWorker.Run(worker.InterruptCh())
}
// Unit testing the main workflow.
func (suite *MyWorkflowTestSuite) TestSomething() {
runner := NewMockPhaseRunner(suite.T())
// set expectations on runner
base := &Base {
phaseRunner: runner,
}
suite.env.ExecuteWorkflow(base.MyWorkflow, ...)
}
It’s a bit convoluted but does the job. Does this look reasonable? Any better way to approach this?
Thanks for your help!