Golang中如何在测试用例模拟os包函数调用

Golang中如何在测试用例模拟os包函数调用 我想为以下代码编写测试用例:

func WriteStringInFile(content string, fileName string) error {

	f, err := os.Create(fileName)
	defer f.Close()
	if err != nil {
		return err
	}
	_, err = f.WriteString(content)
	if err != nil {
		return err
	}
	return nil
}

我想实现类似这样的效果:

when(os.Create(fileName)).thenDoNothing()

在Go语言中是否有好的模拟框架来模拟这些函数调用? 或者我能否不使用任何框架来实现?


更多关于Golang中如何在测试用例模拟os包函数调用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

是的,你说得对,我可以通过内存缓冲区来实现这一点。 谢谢。

更多关于Golang中如何在测试用例模拟os包函数调用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


对于每次调用,我都需要创建一个文件并将字符串写入其中。
但在运行测试用例时,我不希望创建任何运行时资源(例如文件)。
其次,在部署环境中我无法创建和读取文件。
否则,我的测试代码在本地运行良好。

您也可以使用虚拟文件系统。

GitHub

blang/vfs

头像

blang/vfs

vfs - 使用Golang编写的虚拟文件系统库

可以使用变量来保存测试时需要替换的函数。例如,如下所示。话虽如此,我认为我从未需要替换 os.Open。您测试的场景是什么?我建议相信 os.Open 能够按照其宣传的方式工作。如果您希望它返回一个内存缓冲区而不是文件,以便检查写入的内容,更好的做法是重新设计您的函数,使其接受一个 io.Writer,并由调用者打开文件。这样,在测试时您可以传递一个 bytes.Buffer 或类似的内容。

var osCreate = os.Create

func WriteStringInFile(content string, fileName string) error {
	f, err := osCreate(fileName)
	defer f.Close()
	if err != nil {
    ...

在测试代码中:

func TestBrokenOpen(t *testing.T) {
    osOpen = myBrokenOpen
    defer func() { osOpen = os.Open }()
    ...
}

在Go语言中,可以通过接口抽象和依赖注入的方式模拟os包函数调用,无需使用外部框架。以下是两种实现方案:

方案1:使用接口抽象(推荐)

// 定义文件操作接口
type FileSystem interface {
    Create(name string) (File, error)
}

type File interface {
    WriteString(s string) (int, error)
    Close() error
}

// 实际实现包装os包
type OSFileSystem struct{}

func (fs *OSFileSystem) Create(name string) (File, error) {
    return os.Create(name)
}

// 生产代码使用接口
func WriteStringInFile(fs FileSystem, content string, fileName string) error {
    f, err := fs.Create(fileName)
    if err != nil {
        return err
    }
    defer f.Close()
    
    _, err = f.WriteString(content)
    if err != nil {
        return err
    }
    return nil
}

方案2:测试中使用模拟实现

// 模拟文件系统
type MockFileSystem struct {
    CreateFunc func(name string) (File, error)
}

func (m *MockFileSystem) Create(name string) (File, error) {
    if m.CreateFunc != nil {
        return m.CreateFunc(name)
    }
    return nil, errors.New("not implemented")
}

// 模拟文件
type MockFile struct {
    WriteStringFunc func(s string) (int, error)
    CloseFunc       func() error
}

func (m *MockFile) WriteString(s string) (int, error) {
    if m.WriteStringFunc != nil {
        return m.WriteStringFunc(s)
    }
    return 0, nil
}

func (m *MockFile) Close() error {
    if m.CloseFunc != nil {
        return m.CloseFunc()
    }
    return nil
}

测试用例示例

func TestWriteStringInFile(t *testing.T) {
    tests := []struct {
        name        string
        content     string
        fileName    string
        setupMock   func(*MockFileSystem, *MockFile)
        wantErr     bool
    }{
        {
            name:     "success case",
            content:  "test content",
            fileName: "test.txt",
            setupMock: func(mockFS *MockFileSystem, mockFile *MockFile) {
                mockFS.CreateFunc = func(name string) (File, error) {
                    if name != "test.txt" {
                        return nil, errors.New("wrong filename")
                    }
                    return mockFile, nil
                }
                mockFile.WriteStringFunc = func(s string) (int, error) {
                    if s != "test content" {
                        return 0, errors.New("wrong content")
                    }
                    return len(s), nil
                }
                mockFile.CloseFunc = func() error {
                    return nil
                }
            },
            wantErr: false,
        },
        {
            name:     "create file error",
            content:  "test content",
            fileName: "test.txt",
            setupMock: func(mockFS *MockFileSystem, mockFile *MockFile) {
                mockFS.CreateFunc = func(name string) (File, error) {
                    return nil, errors.New("permission denied")
                }
            },
            wantErr: true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            mockFS := &MockFileSystem{}
            mockFile := &MockFile{}
            tt.setupMock(mockFS, mockFile)
            
            err := WriteStringInFile(mockFS, tt.content, tt.fileName)
            
            if (err != nil) != tt.wantErr {
                t.Errorf("WriteStringInFile() error = %v, wantErr %v", err, tt.wantErr)
            }
        })
    }
}

实际调用示例

// 在生产环境中使用真实实现
func main() {
    fs := &OSFileSystem{}
    err := WriteStringInFile(fs, "Hello World", "output.txt")
    if err != nil {
        log.Fatal(err)
    }
}

这种方法通过接口抽象实现了对文件系统操作的完全控制,测试时可以注入模拟对象,生产代码使用真实实现,无需修改外部依赖。

回到顶部