Golang如何测试返回Channel类型的函数?解决测试阻塞问题
测试返回Channel类型的Golang函数问题
我尝试测试StartP函数,期望Start()被调用1次、Done()被调用1次,但运行到<-ps.Done()步骤时测试会阻塞,我期望<-ps.Done()返回nil。请问如何测试返回Channel类型的Golang函数?
生产代码
// production code func (s *vService) StartP(ctx context.Context, reason string) error { ps, err := s.factory.CreateVService(ctx) if err != nil { return err } ps.Start(reason) err = <-ps.Done() // 代码在这里阻塞,如何测试? if err != nil { return err } return nil }
测试代码(原版本)
// test code func Test_StartP(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockPService := mockpservice.NewMockPInterface(mockCtrl) vService := &vService { factory: &servicefactory.FakeServiceFactory{ MockPService: mockPService } } mockPService.EXPECT().Start("reason").Times(1).Return() mockPService.EXPECT().Done().Times(1).DoAndReturn(func() chan error { return nil }) err := vService.StartP(context.Background(), "reason") assert.Equal(t, nil, err) }
相关接口与Mock代码
// 定义的接口 type PServiceInterface interface { Start(reason string) Done() <-chan error }
gomock生成的Mock方法:
func (m *MockProvisionServiceInterface) Done() <-chan error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Done") ret0, _ := ret[0].(<-chan error) fmt.Println(ret0,".....mock Done()") return ret0 }
我还尝试过以下写法,但问题仍未解决:
mockProvisionService.EXPECT().Done().Times(1).DoAndReturn( func() chan error { fmt.Println("DoAndReturn...err nil") ch := make(chan error, 1) ch <- nil return ch })
解决方案
问题核心在于通道的处理逻辑:
- 返回
nil通道:Go语言中读取nil通道会永久阻塞,这是语言特性,绝对不能返回nil通道。 - 你尝试的缓冲通道写法本身正确,但需要确保通道类型与接口定义完全匹配。
修改后的测试代码
调整Done()的Mock逻辑,返回预填充nil值的只读通道:
func Test_StartP(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockPService := mockpservice.NewMockPInterface(mockCtrl) vService := &vService { factory: &servicefactory.FakeServiceFactory{ MockPService: mockPService } } mockPService.EXPECT().Start("reason").Times(1) // 直接返回带nil值的只读通道 mockPService.EXPECT().Done().Times(1).Return(func() <-chan error { ch := make(chan error, 1) ch <- nil return ch }()) err := vService.StartP(context.Background(), "reason") assert.Equal(t, nil, err) }
关键说明
- 类型严格匹配:接口定义
Done()返回<-chan error(只读通道),Mock返回的通道需保持一致,避免隐式转换引发的问题。 - 预填充通道值:使用缓冲为1的通道,提前写入
nil,确保生产代码中的<-ps.Done()能立刻读取到值,不会阻塞。 - 简化Mock逻辑:直接在
Return()中生成并返回填充好的通道,比DoAndReturn()更简洁,也减少了类型转换风险。
如果需要模拟错误场景,只需将ch <- nil改为ch <- errors.New("test error"),生产代码就能正确捕获该错误。
内容的提问来源于stack exchange,提问作者Y. Ryan
相关产品推荐
相关产品推荐

