Skip to Content
GuidesTesting Best Practices

Best Practices: Testing Strategies

For: QA engineers and developers
Level: Advanced
Time to read: 35 minutes
Coverage: 80%+ testing target

This guide covers comprehensive testing strategies from unit tests to production monitoring.


Testing Pyramid & Strategy

▲ Cost/Time ╱ ╲ ╱ E2E╲ 5-10 tests ╱─────╲ Complete workflows ╱ ╲ Production-like ╱ Integration╲ 30-50 tests ╱─────────────╲ Component integration ╱ ╲ Staging environment ╱ Unit Tests ╲ 100-200 tests ───────────────── Isolated components

Unit Testing

Activity Unit Tests

func TestValidateOrder(t *testing.T) { tests := []struct { name string input *ValidateOrderInput wantValid bool wantErr bool }{ { name: "valid order", input: &ValidateOrderInput{ OrderID: "ORD-123", Amount: 100.00, }, wantValid: true, wantErr: false, }, { name: "missing order ID", input: &ValidateOrderInput{ Amount: 100.00, }, wantValid: false, wantErr: true, }, { name: "negative amount", input: &ValidateOrderInput{ OrderID: "ORD-123", Amount: -50.00, }, wantValid: false, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := activities.ValidateOrder(context.Background(), tt.input) if tt.wantErr { assert.Error(t, err) return } assert.NoError(t, err) assert.Equal(t, tt.wantValid, result.Valid) }) } }

Test Coverage Target

# Run tests with coverage go test -cover ./... # Detailed coverage go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out # Target: 80%+ coverage # Priority: Core activities, error paths, edge cases

Integration Testing

Activity + Database Integration

func TestLoadCustomerWithDB(t *testing.T) { // Setup test database db := setupTestDB(t) defer db.Close() // Insert test data _, err := db.Exec(` INSERT INTO customers (id, name, email, tier) VALUES ('cust-1', 'Alice', 'alice@example.com', 'gold') `) assert.NoError(t, err) // Create activity context ctx := database.WithContext(context.Background(), db) // Call activity result, err := activities.LoadCustomer(ctx, &LoadCustomerInput{ CustomerID: "cust-1", }) // Assertions assert.NoError(t, err) assert.Equal(t, "Alice", result.Name) assert.Equal(t, "gold", result.Tier) }

Workflow Integration Test

func TestOrderWorkflow(t *testing.T) { s := &testsuite.WorkflowTestSuite{} env := s.NewTestWorkflowEnvironment() // Mock activities env.OnActivity(activities.ValidateOrder, mock.Anything). Return(&ValidateOrderOutput{Valid: true}, nil) env.OnActivity(activities.ProcessPayment, mock.Anything). Return(&ProcessPaymentOutput{ PaymentID: "pay-123", Status: "success", }, nil) // Execute workflow env.ExecuteWorkflow(workflows.OrderProcessing, OrderInput{ OrderID: "ord-123", Amount: 100.00, }) // Assertions assert.True(t, env.IsWorkflowCompleted()) assert.NoError(t, env.GetWorkflowError()) // Verify activity calls env.AssertExpectations(t) }

End-to-End Testing

E2E Test Script

#!/bin/bash set -e # Deploy application echo "Deploying application..." cascade app apply -f test-app.yaml --namespace test-e2e # Wait for readiness kubectl wait --for=condition=ready pod -l app=cascade --namespace test-e2e --timeout=300s # Start workflow echo "Starting workflow..." EXECUTION_ID=$(cascade process start \ --app test-app \ --workflow TestOrderFlow \ --input '{"order_id":"e2e-123","amount":100.00}' \ --namespace test-e2e \ --quiet) echo "Execution ID: $EXECUTION_ID" # Wait for completion echo "Waiting for completion..." cascade process wait $EXECUTION_ID --timeout=5m --namespace test-e2e # Verify results echo "Verifying results..." STATUS=$(cascade process inspect $EXECUTION_ID --json --namespace test-e2e | jq -r '.status') if [ "$STATUS" != "completed" ]; then echo "FAIL: Workflow did not complete. Status: $STATUS" cascade logs $EXECUTION_ID --namespace test-e2e exit 1 fi # Check database state ORDER=$(psql -h postgres.test-e2e -c "SELECT * FROM orders WHERE id='e2e-123'") if [ -z "$ORDER" ]; then echo "FAIL: Order not found in database" exit 1 fi echo "SUCCESS: E2E test passed"

Performance Testing

Load Test

#!/bin/bash # Test with increasing concurrency for CONCURRENCY in 10 50 100 500 1000; do echo "Testing with concurrency: $CONCURRENCY" # Start N workflows in parallel for i in $(seq 1 $CONCURRENCY); do cascade process start \ --app test-app \ --workflow LoadTest \ --input "{\"test_id\":$i}" \ --namespace load-test & done wait # Collect metrics sleep 10 # Wait for completion COMPLETED=$(cascade process list \ --namespace load-test \ --status completed \ --since 1m | wc -l) AVG_LATENCY=$(cascade metrics workflow \ --namespace load-test \ --period 1m \ --percentile p50 \ --json | jq '.duration_ms') echo " Completed: $COMPLETED, P50: ${AVG_LATENCY}ms" # Check for errors ERRORS=$(cascade process list \ --namespace load-test \ --status failed \ --since 1m | wc -l) if [ $ERRORS -gt 0 ]; then echo " WARNING: $ERRORS failures detected" fi done

Benchmark Activity

func BenchmarkValidateOrder(b *testing.B) { input := &ValidateOrderInput{ OrderID: "ORD-123", Amount: 100.00, } ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { activities.ValidateOrder(ctx, input) } // Run: go test -bench=. -benchmem // Output: BenchmarkValidateOrder-8 100000 11234 ns/op 1024 B/op 15 allocs/op }

Chaos Testing

Workflow Under Failure

# chaos-test.yaml apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: name: cascade-pod-chaos spec: action: kill mode: fixed value: 1 selector: namespaces: - cascade labelSelectors: app: cascade-worker scheduler: cron: "@hourly"

Database Latency Injection

# Add 100ms latency to database kubectl patch service postgres -p '{"spec":{"ports":[{"port":5432,"protocol":"TCP","targetPort":5432,"nodePort":32300}]}}' tc qdisc add dev eth0 root netem delay 100ms

Quality Gates

Pre-Deployment Checks

#!/bin/bash # Code quality go vet ./... go fmt ./... # Coverage COVERAGE=$(go test -cover ./... | grep total | awk '{print $3}' | tr -d '%') if [ "$COVERAGE" -lt 80 ]; then echo "FAIL: Coverage $COVERAGE% < 80%" exit 1 fi # Security scanning gosec ./... # Linting golangci-lint run # Run tests go test -v -race -timeout 10m ./... echo "SUCCESS: All quality gates passed"

Testing Checklist

Unit Tests

  • Happy path tests
  • Error path tests
  • Boundary conditions
  • Null/empty inputs
  • Edge cases

Integration Tests

  • Database operations
  • External API mocks
  • Error scenarios
  • Data consistency
  • Transaction handling

E2E Tests

  • Happy path workflow
  • Error recovery
  • Timeouts
  • Concurrent workflows
  • Data validation

Performance Tests

  • Load tests (100+ concurrent)
  • Latency benchmarks
  • Memory profiling
  • Database query performance
  • Cache effectiveness

Security Tests

  • Authentication
  • Authorization
  • Data isolation
  • SQL injection prevention
  • Sensitive data redaction

Best Practices

DO:

  • Write tests first (TDD)
  • Test error paths
  • Mock external dependencies
  • Use table-driven tests
  • Run tests in CI/CD
  • Monitor test coverage
  • Test at scale
  • Document test strategy

DON’T:

  • Skip tests to save time
  • Only test happy paths
  • Couple tests to implementation
  • Write tests after code
  • Ignore flaky tests
  • Test third-party libraries
  • Mock too much

Updated: October 29, 2025
Version: 1.0
Coverage Target: 80%+

Last updated on