In Go testing, functions like t.Fatal and t.FailNow call runtime.Goexit() to immediately terminate the
current goroutine and mark the test as failed. However, when these functions are called from a goroutine other than the main test goroutine, they only
terminate that specific goroutine, not the entire test.
This creates several problems:
- The test may continue running even after a failure is detected, leading to unpredictable behavior
- Other goroutines may continue executing, potentially causing resource leaks or interfering with subsequent tests
- The test may hang indefinitely if the main goroutine is waiting for the failed goroutine to complete
- Error reporting becomes unclear since the test doesn’t immediately fail when the issue is detected
The Go testing package is designed with the assumption that test control functions are called from the main test goroutine. When this assumption is
violated, the testing framework cannot properly manage the test lifecycle.
What is the potential impact?
Calling t.Fatal from separate goroutines can cause tests to hang indefinitely, produce unreliable results, or mask real failures. This
leads to flaky tests that are difficult to debug and can block continuous integration pipelines. In severe cases, hanging tests may consume system
resources and require manual intervention to terminate.