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

Go语言含channel、目录遍历、API调用的程序如何编写单元测试

代码改造与单元测试方案

1. 代码结构调整(仅小改动,不影响原有运行逻辑)

当前代码难以测试的核心问题是全局依赖过多、错误处理耦合了panic、第三方依赖未做抽象,仅需做以下几处小调整即可:

  • 改造WalkFilePath函数,移除全局依赖,改为参数注入,同时将内部panic改为错误返回:
    // 改造后的函数签名,原有逻辑不变,仅把全局的rootpath、paths改为入参,错误对外返回
    func WalkFilePath(root string, paths chan<- string) error {
        return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return fmt.Errorf("Failed to walk directory: %T %w", err, err)
            }
            if !info.IsDir() {
                paths <- path
            }
            return nil
        })
    }
    
    原有main函数调用时仍传入全局的*rootpath和全局paths变量,收到错误后再panic即可,原有功能完全不受影响。
  • 移除全局的paths变量,在main函数内部声明即可,放开你注释掉的paths := make(chan string)代码即可。
  • 抽象putio.Client的上传能力为接口,避免测试时发起真实上传请求:
    // 定义接口,putio原生客户端天然实现该接口
    type Uploader interface {
        Upload(ctx context.Context, f *os.File, path string, parentID int64) (*putio.Upload, error)
    }
    // worker函数的client参数改为Uploader接口类型,原有逻辑不变
    func worker(paths <-chan string, wg *sync.WaitGroup, client Uploader) {
        // 原有代码完全不变
    }
    

2. WalkFilePath单元测试编写

测试不需要启动goroutine,仅需单独验证目录遍历、文件路径发送逻辑即可:

  1. 用testing.T.TempDir()创建临时测试目录,构造预设的文件/目录结构
  2. 初始化带缓冲的channel,缓冲大小大于测试文件数量即可避免阻塞
  3. 调用WalkFilePath传入临时目录与channel,收集channel内的所有路径和预期结果对比
    示例测试代码:
package main

import (
    "path/filepath"
    "os"
    "sort"
    "reflect"
    "testing"
)

func TestWalkFilePath(t *testing.T) {
    // 构造测试目录结构
    tempDir := t.TempDir()
    testFiles := []string{
        "a.txt",
        "subdir/b.md",
        "subdir/c.go",
    }
    expectedPaths := make([]string, 0, len(testFiles))
    for _, f := range testFiles {
        fullPath := filepath.Join(tempDir, f)
        if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
            t.Fatalf("create test dir failed: %v", err)
        }
        if err := os.WriteFile(fullPath, []byte("test content"), 0644); err != nil {
            t.Fatalf("create test file failed: %v", err)
        }
        expectedPaths = append(expectedPaths, fullPath)
    }

    // 调用待测试函数
    pathsChan := make(chan string, len(expectedPaths))
    err := WalkFilePath(tempDir, pathsChan)
    close(pathsChan)

    // 校验错误返回
    if err != nil {
        t.Fatalf("WalkFilePath returned unexpected error: %v", err)
    }

    // 收集返回结果对比
    gotPaths := make([]string, 0)
    for p := range pathsChan {
        gotPaths = append(gotPaths, p)
    }
    sort.Strings(expectedPaths)
    sort.Strings(gotPaths)
    if !reflect.DeepEqual(expectedPaths, gotPaths) {
        t.Errorf("WalkFilePath got paths %v, expected %v", gotPaths, expectedPaths)
    }
}

3. worker函数单元测试编写

使用Mock实现模拟上传行为,验证文件读取、上传逻辑是否符合预期:

  1. 实现Mock上传器,记录调用参数、模拟错误返回
type mockUploader struct {
    UploadedPaths []string
    MockErr       error
}

func (m *mockUploader) Upload(ctx context.Context, f *os.File, path string, parentID int64) (*putio.Upload, error) {
    m.UploadedPaths = append(m.UploadedPaths, path)
    if m.MockErr != nil {
        return nil, m.MockErr
    }
    return &putio.Upload{File: &putio.File{Name: filepath.Base(path)}}, nil
}
  1. 测试时构造临时测试文件,传入worker的channel,验证Mock调用情况是否符合预期即可,也可以劫持日志输出验证错误、成功日志是否正确打印。

4. 主函数测试

主函数多为初始化逻辑和流程编排,不需要编写单元测试,仅需做简单集成测试验证流程通顺即可。

内容的提问来源于stack exchange,提问作者baris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 14:45:01