Debug Playbook for Flaky CI Tests in Parallel Environments
1. Timing Dependencies
- Symptom: Intermittent timeouts, assertion failures on eventual consistency, or resource unavailability, especially with external services or asynchronous operations.
- Hypotheses:
* Hypothesis A: A test asserts on state before an asynchronous operation completes. * Hypothesis B: Resource contention under parallel load (e.g., port conflicts, database connection limits).
* Check 1: grep -r "time.Sleep" . (Identifies fixed Sleep calls potentially masking race conditions.) * Check 2: docker-compose logs --tail 1000 <service_name> | grep "error\|timeout" (Monitors dependent service logs for resource-related errors during CI runs.)
* Fix 1: Implement explicit wait-for-condition logic with timeouts instead of fixed delays. * Fix 2: Increase CI environment resource limits or use robust health checks for services before tests.
- Verification: Rerun the test suite multiple times in parallel on CI. Confirm previous error patterns are absent.
2. Shared State Contamination
- Symptom: Tests pass individually but fail when run in a suite, exhibiting corrupted data, unexpected environment variables, or incorrect object states.
- Hypotheses:
* Hypothesis A: Tests fail to clean up or isolate their state (e.g., database entries, temporary files), affecting subsequent tests. * Hypothesis B: Parallel tests modify the same shared resource (e.g., a single test database, global cache), leading to race conditions.
* Check 1: go test -count=1 -run "TestSpecificFlakyCase" (Run flaky test in isolation. If it passes, run with neighbors to identify interaction.) * Check 2: git grep -lE "(db\.Connect|os\.Setenv|globalVar)" . (Searches for global variables, singletons, or environment variable modifications.)
* Fix 1: Implement robust test isolation: transaction rollbacks for database tests or dedicated temporary resources. * Fix 2: Refactor to avoid global state. Pass dependencies explicitly. Ensure TestMain or equivalent resets global state.
- Verification: Run the test suite with increased parallelism (e.g.,
-p 8) on CI. Observe consistent passes across multiple builds.
3. Non-Deterministic Test Ordering
- Symptom: Tests fail only when run in a specific, often random, order, but pass when the order changes or in isolation, indicating implicit dependencies.
- Hypotheses:
* Hypothesis A: Test A implicitly relies on side effects or state set up by Test B. * Hypothesis B: A test modifies a shared resource without cleanup, and another test expects a pristine state.
* Check 1: go test -shuffle=on -v ./... (Run tests with randomized ordering if supported to expose dependencies.) * Check 2: Analyze CI logs for execution sequence patterns in failed versus successful builds.
* Fix 1: Make each test self-contained and independent with complete setup and teardown. * Fix 2: Refactor shared setup/teardown logic to ensure isolation, e.g., fresh database instances or in-memory databases.
- Verification: Run the test suite multiple times in parallel and with randomized ordering. Confirm consistent passes regardless of execution sequence.