Hello
I’m trying to wrap my head around unit testing workflows and activities.
I’m trying to mimic the “entity workflow pattern” as describe in the typescript sdk: Workflows in TypeScript | Temporal Documentation
Code looks something like this:
...
func (svc *Service) Workflow(ctx workflow.Context, raised_event *event.Event) error {
if err := workflow.ExecuteActivity(ctx, svc.ActivityOne, raised_event).Get(ctx, &return_value); err != nil {
return err
}
signals := workflow.GetSignalChannel(ctx, config.UPDATE_CHANNEL_NAME)
selector := workflow.NewSelector(ctx)
var update_event *event.Event
selector.AddReceive(signals, func(channel workflow.ReceiveChannel, _ bool) {
channel.Receive(ctx, &update_event)
})
for it := 0; it < config.MAX_ITERATIONS; it++ {
selector.Select(ctx)
workflow.ExecuteActivity(ctx, svc.ActivityTwo, update_event)
if update_event.IsDone() {
return nil
}
workflow.Sleep(ctx, time.Minute)
}
return workflow.NewContinueAsNewError(ctx, svc.Workflow, raised_event)
}
And test:
const day = time.Hour * 24
...
func (s *WorkflowTestSuite) Test_Workflow_Success() {
raised_event := &event.Event{...}
in_progress_event := &event.Event{...}
in_review_event := &event.Event{...}
done_event := &event.Event{...}
s.env.RegisterActivity(s.svc.ActivityOne)
s.env.RegisterActivity(s.svc.ActivityTwo)
s.env.OnActivity(s.svc.ActivityOne, mock.Anything, raised_event).Return("return value", nil)
s.env.OnActivity(s.svc.ActivityTwo, mock.Anything, in_progress_event).Return(nil)
s.env.OnActivity(s.svc.ActivityTwo, mock.Anything, in_review_event).Return(nil)
s.env.OnActivity(s.svc.ActivityTwo, mock.Anything, done_event).Return(nil)
s.env.RegisterDelayedCallback(func() {
s.env.SignalWorkflow(config.UPDATE_CHANNEL_NAME, in_progress_event)
}, day)
s.env.RegisterDelayedCallback(func() {
s.env.SignalWorkflow(config.UPDATE_CHANNEL_NAME, in_review_event)
}, 2*day)
s.env.RegisterDelayedCallback(func() {
s.env.SignalWorkflow(config.UPDATE_CHANNEL_NAME, done_event)
}, 3*day)
s.env.ExecuteWorkflow(s.svc.Workflow, raised_event)
if err := s.env.GetWorkflowError(); err != nil {
s.Require().True(workflow.IsContinueAsNewError(err))
}
}
--- FAIL: TestWorkflowTestSuite (0.00s)
--- FAIL: TestWorkflowTestSuite/Test_Workflow_Success (0.00s)
***/pkg/temporal/workflow_testsuite.go:815: PASS: ActivityOne(string,*event.Event)
***/pkg/temporal/workflow_testsuite.go:815: PASS: ActivityTwo(string,*event.Event)
***/pkg/temporal/workflow_testsuite.go:815: PASS: ActivityTwo(string,*event.Event)
***/pkg/temporal/workflow_testsuite.go:815: FAIL: ActivityTwo(string,*event.Event)
at: [workflow_testsuite.go:297 workflow_test.go:65]
***/pkg/temporal/workflow_testsuite.go:815: FAIL: 3 out of 4 expectation(s) were met.
The code you are testing needs to make 1 more call(s).
at: [workflow_testsuite.go:815 workflow_test.go:34 suite.go:137 suite.go:159]
My guess is the workflow manages to iterate through it’s max value and errors out with continue as new?
Any tips on how to make this work?
Unfortunate Go doesn’t have an intuitive future pattern. At least TypeScript would warn you on an unawaited promise, something Go does not for async code (especially not our bespoke futures).