This skill should be used when the user asks about "testing workflows", "TestWorkflowEnvironment", "workflow tests", "activity mocking", "replay testing", "temporal testing", "unit test workflow", or needs guidance on testing Temporal applications.
From timelordnpx claudepluginhub therealbill/mynet --plugin timelordThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Benchmarks web page Core Web Vitals/bundle sizes, API latency under load, build times; detects regressions via before/after PR comparisons.
Guidance for testing Temporal workflows and activities using the Go SDK testing framework.
Temporal provides a comprehensive testing framework:
The test environment simulates Temporal without a server.
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"go.temporal.io/sdk/testsuite"
)
type WorkflowTestSuite struct {
suite.Suite
testsuite.WorkflowTestSuite
}
func (s *WorkflowTestSuite) SetupTest() {
s.SetLogger(log.NewNopLogger())
}
func TestWorkflowTestSuite(t *testing.T) {
suite.Run(t, new(WorkflowTestSuite))
}
func (s *WorkflowTestSuite) TestOrderWorkflow_Success() {
env := s.NewTestWorkflowEnvironment()
// Mock activities
env.OnActivity(ValidateOrder, mock.Anything, mock.Anything).Return(nil)
env.OnActivity(ProcessPayment, mock.Anything, mock.Anything).Return(
&PaymentResult{TransactionID: "txn-123"}, nil,
)
env.OnActivity(ShipOrder, mock.Anything, mock.Anything).Return(
&ShipmentResult{TrackingNumber: "track-456"}, nil,
)
// Execute workflow
order := Order{ID: "order-1", Amount: 100}
env.ExecuteWorkflow(OrderWorkflow, order)
// Assert completion
s.True(env.IsWorkflowCompleted())
s.NoError(env.GetWorkflowError())
// Get result
var result OrderResult
s.NoError(env.GetWorkflowResult(&result))
s.Equal("txn-123", result.TransactionID)
s.Equal("track-456", result.TrackingNumber)
}
func (s *WorkflowTestSuite) TestOrderWorkflow_PaymentFails() {
env := s.NewTestWorkflowEnvironment()
// Mock successful validation
env.OnActivity(ValidateOrder, mock.Anything, mock.Anything).Return(nil)
// Mock payment failure
env.OnActivity(ProcessPayment, mock.Anything, mock.Anything).Return(
nil, errors.New("payment declined"),
)
// Execute workflow
order := Order{ID: "order-1", Amount: 100}
env.ExecuteWorkflow(OrderWorkflow, order)
// Assert failure
s.True(env.IsWorkflowCompleted())
s.Error(env.GetWorkflowError())
s.Contains(env.GetWorkflowError().Error(), "payment declined")
}
Mock activities to isolate workflow logic.
// Mock with exact arguments
env.OnActivity(SendEmail, mock.Anything, EmailRequest{
To: "user@example.com",
Subject: "Order Confirmed",
}).Return(nil)
// Mock with any arguments
env.OnActivity(SendEmail, mock.Anything, mock.Anything).Return(nil)
// Different returns based on input
env.OnActivity(GetUser, mock.Anything, "user-1").Return(
&User{ID: "user-1", Name: "Alice"}, nil,
)
env.OnActivity(GetUser, mock.Anything, "user-2").Return(
&User{ID: "user-2", Name: "Bob"}, nil,
)
env.OnActivity(GetUser, mock.Anything, "user-unknown").Return(
nil, errors.New("user not found"),
)
// Dynamic mock behavior
callCount := 0
env.OnActivity(ProcessItem, mock.Anything, mock.Anything).Return(
func(ctx context.Context, item Item) (*Result, error) {
callCount++
if item.Priority == "high" {
return &Result{Status: "expedited"}, nil
}
return &Result{Status: "normal"}, nil
},
)
Test workflows that receive signals.
func (s *WorkflowTestSuite) TestApprovalWorkflow_Approved() {
env := s.NewTestWorkflowEnvironment()
// Register callback to send signal
env.RegisterDelayedCallback(func() {
env.SignalWorkflow("approval", ApprovalSignal{
Approved: true,
ApproverID: "manager-1",
})
}, time.Millisecond*100)
// Execute workflow
env.ExecuteWorkflow(ApprovalWorkflow, "doc-123")
s.True(env.IsWorkflowCompleted())
s.NoError(env.GetWorkflowError())
var result ApprovalResult
s.NoError(env.GetWorkflowResult(&result))
s.True(result.Approved)
}
func (s *WorkflowTestSuite) TestApprovalWorkflow_Rejected() {
env := s.NewTestWorkflowEnvironment()
env.RegisterDelayedCallback(func() {
env.SignalWorkflow("approval", ApprovalSignal{
Approved: false,
Reason: "Budget exceeded",
})
}, time.Millisecond*100)
env.ExecuteWorkflow(ApprovalWorkflow, "doc-123")
s.True(env.IsWorkflowCompleted())
var result ApprovalResult
s.NoError(env.GetWorkflowResult(&result))
s.False(result.Approved)
s.Equal("Budget exceeded", result.RejectionReason)
}
Test query handlers in workflows.
func (s *WorkflowTestSuite) TestStateMachineWorkflow_Query() {
env := s.NewTestWorkflowEnvironment()
// Start workflow (don't complete it)
env.RegisterDelayedCallback(func() {
// Query current state
result, err := env.QueryWorkflow("getState")
s.NoError(err)
var state string
s.NoError(result.Get(&state))
s.Equal("pending", state)
// Send signal to change state
env.SignalWorkflow("approve", nil)
}, time.Millisecond*50)
env.RegisterDelayedCallback(func() {
// Query state after signal
result, err := env.QueryWorkflow("getState")
s.NoError(err)
var state string
s.NoError(result.Get(&state))
s.Equal("approved", state)
// Complete the workflow
env.SignalWorkflow("complete", nil)
}, time.Millisecond*100)
env.ExecuteWorkflow(StateMachineWorkflow, "item-123")
s.True(env.IsWorkflowCompleted())
}
Test workflows with time-based logic.
func (s *WorkflowTestSuite) TestReminderWorkflow_SendsReminders() {
env := s.NewTestWorkflowEnvironment()
remindersSent := 0
env.OnActivity(SendReminder, mock.Anything, mock.Anything).Return(
func(ctx context.Context, msg string) error {
remindersSent++
return nil
},
)
// Execute workflow that sends 3 daily reminders
env.ExecuteWorkflow(ReminderWorkflow, "task-123")
s.True(env.IsWorkflowCompleted())
s.Equal(3, remindersSent)
}
func (s *WorkflowTestSuite) TestTimeoutWorkflow_TimesOut() {
env := s.NewTestWorkflowEnvironment()
// No signal sent - should timeout
env.ExecuteWorkflow(TimeoutWorkflow, time.Hour)
s.True(env.IsWorkflowCompleted())
s.Error(env.GetWorkflowError())
s.Contains(env.GetWorkflowError().Error(), "timeout")
}
Test parent-child workflow relationships.
func (s *WorkflowTestSuite) TestParentWorkflow_ExecutesChildren() {
env := s.NewTestWorkflowEnvironment()
// Mock child workflow
env.OnWorkflow(ChildWorkflow, mock.Anything).Return(
&ChildResult{Success: true}, nil,
)
// Execute parent
env.ExecuteWorkflow(ParentWorkflow, ParentInput{ItemCount: 3})
s.True(env.IsWorkflowCompleted())
s.NoError(env.GetWorkflowError())
var result ParentResult
s.NoError(env.GetWorkflowResult(&result))
s.Equal(3, result.ProcessedCount)
}
Sync operations are plain functions — test them directly:
func TestEchoOp(t *testing.T) {
result, err := echoHandler(context.Background(), EchoInput{Message: "hello"}, nexus.StartOperationOptions{})
require.NoError(t, err)
require.Equal(t, "hello", result.Message)
}
Async operations start workflows — test the backing workflow as a normal workflow test, and test the operation handler setup separately.
Mock Nexus operations in the test environment:
func (s *WorkflowTestSuite) TestCallerWorkflow_NexusSuccess() {
env := s.NewTestWorkflowEnvironment()
// Register Nexus operation handler for testing
env.RegisterNexusService(testNexusService)
env.ExecuteWorkflow(CallerWorkflow, CallerInput{OrderID: "order-1"})
s.True(env.IsWorkflowCompleted())
s.NoError(env.GetWorkflowError())
}
Test against a real dev server with multiple namespaces:
# Start dev server
temporal server start-dev
# Create test namespaces and endpoint
temporal operator namespace create --namespace test-caller
temporal operator namespace create --namespace test-handler
temporal operator nexus endpoint create \
--name test-endpoint \
--target-namespace test-handler \
--target-task-queue test-handler-tq
Then run both handler and caller workers, execute the caller workflow, and verify the end-to-end result.
Test activity functions in isolation.
func TestSendEmail_Success(t *testing.T) {
// Create mock SMTP client
mockClient := &MockSMTPClient{}
mockClient.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
// Create activity with dependencies
activities := &EmailActivities{client: mockClient}
// Test activity directly
err := activities.SendEmail(context.Background(), EmailRequest{
To: "user@example.com",
Subject: "Test",
Body: "Hello",
})
require.NoError(t, err)
mockClient.AssertExpectations(t)
}
func TestProcessPayment_InvalidAmount(t *testing.T) {
activities := &PaymentActivities{}
_, err := activities.ProcessPayment(context.Background(), PaymentRequest{
Amount: -100, // Invalid
})
require.Error(t, err)
require.Contains(t, err.Error(), "invalid amount")
}
Validate workflow determinism using recorded history.
Export history from running workflows:
temporal workflow show --workflow-id my-workflow-id --output json > history.json
func TestWorkflow_Replay(t *testing.T) {
replayer := worker.NewWorkflowReplayer()
// Register workflow
replayer.RegisterWorkflow(OrderWorkflow)
// Replay against recorded history
err := replayer.ReplayWorkflowHistoryFromJSONFile(
nil,
"testdata/order_workflow_history.json",
)
require.NoError(t, err)
}
Replay tests catch determinism issues:
func TestWorkflow_ReplayDetectsNonDeterminism(t *testing.T) {
replayer := worker.NewWorkflowReplayer()
replayer.RegisterWorkflow(ModifiedOrderWorkflow) // Changed workflow
err := replayer.ReplayWorkflowHistoryFromJSONFile(
nil,
"testdata/original_workflow_history.json",
)
// Will fail if workflow structure changed incompatibly
if err != nil {
t.Logf("Non-determinism detected: %v", err)
}
}
Test against a real Temporal server.
func TestIntegration_OrderWorkflow(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test")
}
// Connect to test server
c, err := client.Dial(client.Options{
HostPort: "localhost:7233",
})
require.NoError(t, err)
defer c.Close()
// Start worker
w := worker.New(c, "test-queue", worker.Options{})
w.RegisterWorkflow(OrderWorkflow)
w.RegisterActivity(&RealActivities{})
go w.Run(worker.InterruptCh())
// Execute workflow
run, err := c.ExecuteWorkflow(context.Background(), client.StartWorkflowOptions{
ID: "test-order-" + uuid.New().String(),
TaskQueue: "test-queue",
}, OrderWorkflow, Order{ID: "test-1", Amount: 50})
require.NoError(t, err)
// Wait for completion
var result OrderResult
err = run.Get(context.Background(), &result)
require.NoError(t, err)
require.NotEmpty(t, result.TransactionID)
}
Structure tests by category:
myapp/
├── workflows/
│ ├── order_workflow.go
│ └── order_workflow_test.go # Unit tests
├── activities/
│ ├── payment_activities.go
│ └── payment_activities_test.go # Activity tests
├── testdata/
│ └── order_workflow_history.json # Replay histories
└── integration/
└── workflow_integration_test.go # Integration tests
Unit Testing:
Activity Testing:
Replay Testing:
Integration Testing:
For detailed testing patterns, consult:
references/test-patterns.md - Advanced testing patternsreferences/replay-testing.md - Comprehensive replay testing guide