JetBrains GoLand中Go测试异常终止问题求助
PostgreSQL事务测试Go代码的终止问题分析
测试代码
t.Run("Case 2: Single Booking Twice - One Should fail", func(t *testing.T) { arg := AddToCalendarParams{ RoomID: newRoom.ID, BookedIntervals: []pgtype.Range[pgtype.Timestamp]{ { Lower: pgtype.Timestamp{ Time: time.Date(3001, 1, 1, 9, 30, 0, 0, time.UTC), InfinityModifier: 0, Valid: true, }, Upper: pgtype.Timestamp{ Time: time.Date(3001, 1, 1, 11, 30, 0, 0, time.UTC), InfinityModifier: 0, Valid: true, }, LowerType: pgtype.Exclusive, UpperType: pgtype.Exclusive, Valid: true, }, }, FromOwner: false, } errorChannel := make(chan error) defer close(errorChannel) go func() { err := store.CreateBookingsTx(context.Background(), arg) errorChannel <- err }() go func() { err := store.CreateBookingsTx(context.Background(), arg) errorChannel <- err }() err := <-errorChannel // the first error should be nil require.NoError(t, err) err = <-errorChannel // the second error should be not nil require.Error(t, err) //fmt.Printf("Error: %v", err) })
问题描述
在JetBrains GoLand中运行上述测试代码时,取消最后一行fmt.Printf("Error: %v", err)的注释后,测试无法正常终止(无报错,仅标记为已停止);注释该语句时测试可正常结束。
原因分析
问题核心在于无缓冲通道的阻塞特性和GoLand测试运行器的异步panic处理逻辑:
- 你创建的
errorChannel是无缓冲通道,发送操作会阻塞直到有接收方接收数据。 - 主goroutine在完成两次通道接收后,会执行
defer close(errorChannel)关闭通道。添加fmt.Printf语句后,主goroutine的执行流程被延迟,导致其中一个goroutine可能还没完成发送操作,通道就已经被关闭。此时该goroutine尝试向已关闭的通道发送数据,会触发异步panic。 - GoLand的测试运行器对这种异步panic的处理不够及时,不会主动终止测试进程,导致测试陷入无响应状态,最终被标记为“已停止”。
- 注释掉
fmt.Printf时,主goroutine执行速度更快,能在通道关闭前完成所有goroutine的发送操作,不会触发panic,测试正常结束。
解决方法
- 将无缓冲通道改为带缓冲通道:
errorChannel := make(chan error, 2),这样两个goroutine的发送操作不会阻塞,即使通道提前关闭,也不会触发发送panic。 - 使用
sync.WaitGroup等待所有goroutine执行完毕后再关闭通道,确保所有发送操作完成:var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() err := store.CreateBookingsTx(context.Background(), arg) errorChannel <- err }() go func() { defer wg.Done() err := store.CreateBookingsTx(context.Background(), arg) errorChannel <- err }() // 等待goroutine完成 wg.Wait() close(errorChannel)
内容的提问来源于stack exchange,提问作者Kostas Oreopoulos
相关产品推荐
相关产品推荐

