You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
}

关键说明

  1. 类型严格匹配:接口定义Done()返回<-chan error(只读通道),Mock返回的通道需保持一致,避免隐式转换引发的问题。
  2. 预填充通道值:使用缓冲为1的通道,提前写入nil,确保生产代码中的<-ps.Done()能立刻读取到值,不会阻塞。
  3. 简化Mock逻辑:直接在Return()中生成并返回填充好的通道,比DoAndReturn()更简洁,也减少了类型转换风险。

如果需要模拟错误场景,只需将ch <- nil改为ch <- errors.New("test error"),生产代码就能正确捕获该错误。


内容的提问来源于stack exchange,提问作者Y. Ryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 00:10:15