Golang实现基础功能与单元测试的最佳实践

Golang实现基础功能与单元测试的最佳实践 我希望开发各种基础且实用的Go函数,以便日后在Go程序中复用。以下是一些例子:

  • 检查磁盘上是否存在特定文件
  • 检查磁盘上是否存在特定目录
  • 在指定位置创建文件
  • 在指定位置创建目录
  • 读取数组并打印所有值及其索引
  • 搜索数组中是否存在特定值
  • 检查主机是否具有互联网连接
  • 检查是否能从URL获取HTTP 200状态码
  • 从URL下载文件
  • 解压文件
  • 等等,等等……

所有这些函数都应该是跨平台的(Windows、Linux、Mac),并且都应该有单元测试。我对单元测试经验不足,因此不确定如何通过模拟文件系统来测试诸如“文件是否存在”或“下载文件”这类功能。如果您能提供类似的示例(例如您的库)或任何相关的技巧/建议——特别是关于测试方面的——我将不胜感激。

我还希望使用日志库,以便区分 infoerrordebug 级别的日志。您推荐使用哪个库来实现这个功能呢?

提前感谢


更多关于Golang实现基础功能与单元测试的最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang实现基础功能与单元测试的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


以下是一些基础功能的Go实现示例及其单元测试,使用标准库和stretchr/testify进行测试,并推荐使用zap作为日志库。

1. 文件系统操作

// fileutil/fileutil.go
package fileutil

import (
    "os"
    "path/filepath"
)

// FileExists 检查文件是否存在
func FileExists(path string) bool {
    _, err := os.Stat(path)
    return !os.IsNotExist(err)
}

// DirExists 检查目录是否存在
func DirExists(path string) bool {
    info, err := os.Stat(path)
    if os.IsNotExist(err) {
        return false
    }
    return info.IsDir()
}

// CreateFile 创建文件
func CreateFile(path string) error {
    dir := filepath.Dir(path)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return err
    }
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    return file.Close()
}

// CreateDir 创建目录
func CreateDir(path string) error {
    return os.MkdirAll(path, 0755)
}

2. 数组操作

// sliceutil/sliceutil.go
package sliceutil

import "fmt"

// PrintWithIndex 打印数组元素和索引
func PrintWithIndex(arr []interface{}) {
    for i, v := range arr {
        fmt.Printf("Index: %d, Value: %v\n", i, v)
    }
}

// Contains 检查数组是否包含特定值
func Contains(arr []interface{}, target interface{}) bool {
    for _, v := range arr {
        if v == target {
            return true
        }
    }
    return false
}

3. 网络操作

// netutil/netutil.go
package netutil

import (
    "io"
    "net/http"
    "os"
    "time"
)

// HasInternetConnection 检查网络连接
func HasInternetConnection() bool {
    client := http.Client{Timeout: 5 * time.Second}
    _, err := client.Get("https://www.google.com")
    return err == nil
}

// IsURLReachable 检查URL是否可达
func IsURLReachable(url string) bool {
    client := http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get(url)
    if err != nil {
        return false
    }
    defer resp.Body.Close()
    return resp.StatusCode == http.StatusOK
}

// DownloadFile 下载文件
func DownloadFile(url, filepath string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    out, err := os.Create(filepath)
    if err != nil {
        return err
    }
    defer out.Close()

    _, err = io.Copy(out, resp.Body)
    return err
}

4. 单元测试示例

// fileutil/fileutil_test.go
package fileutil

import (
    "os"
    "path/filepath"
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestFileExists(t *testing.T) {
    // 创建临时文件
    tmpfile, err := os.CreateTemp("", "testfile")
    require.NoError(t, err)
    defer os.Remove(tmpfile.Name())

    // 测试文件存在
    assert.True(t, FileExists(tmpfile.Name()))
    
    // 测试文件不存在
    assert.False(t, FileExists("/nonexistent/path/file.txt"))
}

func TestDirExists(t *testing.T) {
    // 创建临时目录
    tmpdir, err := os.MkdirTemp("", "testdir")
    require.NoError(t, err)
    defer os.RemoveAll(tmpdir)

    // 测试目录存在
    assert.True(t, DirExists(tmpdir))
    
    // 测试目录不存在
    assert.False(t, DirExists("/nonexistent/path"))
    
    // 测试路径是文件而不是目录
    tmpfile, err := os.CreateTemp("", "testfile")
    require.NoError(t, err)
    defer os.Remove(tmpfile.Name())
    assert.False(t, DirExists(tmpfile.Name()))
}

func TestCreateFile(t *testing.T) {
    tmpdir, err := os.MkdirTemp("", "testcreate")
    require.NoError(t, err)
    defer os.RemoveAll(tmpdir)

    filepath := filepath.Join(tmpdir, "subdir", "test.txt")
    
    // 测试创建文件
    err = CreateFile(filepath)
    assert.NoError(t, err)
    assert.True(t, FileExists(filepath))
}

// netutil/netutil_test.go
package netutil

import (
    "net/http"
    "net/http/httptest"
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestIsURLReachable(t *testing.T) {
    // 创建测试服务器
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }))
    defer server.Close()

    // 测试可达URL
    assert.True(t, IsURLReachable(server.URL))
    
    // 测试不可达URL
    assert.False(t, IsURLReachable("http://localhost:9999/nonexistent"))
}

func TestDownloadFile(t *testing.T) {
    // 创建测试服务器
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("test content"))
    }))
    defer server.Close()

    // 创建临时文件路径
    tmpfile, err := os.CreateTemp("", "downloadtest")
    require.NoError(t, err)
    tmpfile.Close()
    defer os.Remove(tmpfile.Name())

    // 测试下载
    err = DownloadFile(server.URL, tmpfile.Name())
    assert.NoError(t, err)
    
    // 验证文件内容
    content, err := os.ReadFile(tmpfile.Name())
    assert.NoError(t, err)
    assert.Equal(t, "test content", string(content))
}

5. 日志配置

// logger/logger.go
package logger

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

var Log *zap.Logger

func InitLogger(debug bool) error {
    var config zap.Config
    if debug {
        config = zap.NewDevelopmentConfig()
        config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    } else {
        config = zap.NewProductionConfig()
    }

    logger, err := config.Build()
    if err != nil {
        return err
    }
    
    Log = logger
    return nil
}

// 使用示例
func ExampleUsage() {
    // 初始化
    if err := InitLogger(true); err != nil {
        panic(err)
    }
    defer Log.Sync()

    // 记录日志
    Log.Info("程序启动")
    Log.Debug("调试信息", zap.String("key", "value"))
    Log.Error("错误发生", zap.Error(errors.New("test error")))
}

6. go.mod 依赖

module myutils

go 1.21

require (
    go.uber.org/zap v1.26.0
    github.com/stretchr/testify v1.8.4
)

require (
    github.com/davecgh/go-spew v1.1.1 // indirect
    github.com/pmezard/go-difflib v1.0.0 // indirect
    go.uber.org/multierr v1.10.0 // indirect
    gopkg.in/yaml.v3 v3.0.1 // indirect
)

测试说明:

  1. 使用testify/assert进行断言
  2. 文件测试使用临时目录避免污染系统
  3. 网络测试使用httptest模拟服务器
  4. 通过接口抽象使代码可测试
  5. 使用t.Cleanup()defer进行资源清理

日志库推荐使用Uber的zap,性能高且功能完整。对于测试,stretchr/testify提供了丰富的断言和mock功能。

回到顶部