嘲讽bufio.Scanner输入

时间:2019-01-28 16:50:58

标签: go testing mocking

我已经有很多成功定义接口和测试期间代嘲笑,但我已经打了一个问题嘲笑bufio.Scanner输入:

file := &mockFile{
    ReadFunc: func(p []byte) (int, error) {
        reader := bufio.NewReader(bytes.NewReader([]byte(consulPropertiesFile)))
        return reader.Read(p)
    },
    CloseFunc: func() error {
        return nil
    },
}

fs := &mockFileSystem{
    OpenFunc: func(name string) (File, error) {
        return file, nil
    },
}

properties, err := readConsulPropertiesFile(fs)

虽然这似乎行得通,但是一旦扫描仪到达我字符串的末尾,它似乎就回到了开头,并且读取了太多内容(这次似乎读取了一行以上)。这就像我需要手动在适当的时候在返回EOF错误我ReadFunc,但我不知道如何计算出来的时候我应该做的......

扫描仪代码(从here提起):

f, err := file.Open("/run/secrets/consul-web4.properties")
if err != nil {
    return nil, err
}
defer f.Close()

properties := map[string]string{}

scanner := bufio.NewScanner(f)
for scanner.Scan() {
    line := scanner.Text()
    if equal := strings.Index(line, "="); equal >= 0 {
        if key := strings.TrimSpace(line[:equal]); len(key) > 0 {
            value := ""
            if len(line) > equal {
                value = strings.TrimSpace(line[equal+1:])
            }
            properties[key] = value
        }
    }
}

我还没有重构上面的内容...

我已经在测试字符串上尝试了以下变体:

const input = `key=value
key=value
key=value
`
const input = "key=value\nkey=value\nkey=value\n"

我已经尝试了bufio.Reader和io.Reader实现。

任何帮助/洞察理解!

1 个答案:

答案 0 :(得分:1)

感谢@Adrian:

我意识到在回家的路上这可能是由于每次调用该方法时实例化一个新的读取器。将阅读器实例移出我的ReadFunc效果很好!

感谢@Thundercat的strings.NewReader()提示,更新了代码:

reader := ioutil.NopCloser(strings.NewReader(consulPropertiesFile))

file := &mockFile{
    ReadFunc: func(p []byte) (int, error) {
        return reader.Read(p)
    },
    CloseFunc: func() error {
        return nil
    },
}

对于遇到这篇文章的任何人,以查找有关如何模拟文件系统等的信息:

用于打开和统计文件的界面,以及使用os软件包的具体实现:

// FileSystem - interface to the filesystem
type FileSystem interface {
    Open(name string) (File, error)
    Stat(name string) (os.FileInfo, error)
}

// OsFS - filesystem backed by default os package
type OsFS struct {
}

// Open - open a file
func (o *OsFS) Open(name string) (File, error) {
    return os.Open(name)
}

// Stat - get file status
func (o *OsFS) Stat(name string) (os.FileInfo, error) {
    return os.Stat(name)
}

上面提到的文件接口:

// File - file interface
type File interface {
    io.Closer
    io.Reader
    io.ReaderAt
    io.Seeker
    Stat() (os.FileInfo, error)
}

模拟您要文件系统返回的文件(实现代码将使用的方法):

type mockFile struct {
    CloseFunc  func() error
    ReadFunc   func(p []byte) (int, error)
    ReadAtFunc func(p []byte, off int64) (int, error)
    SeekFunc   func(offset int64, whence int) (int64, error)
    StatFunc   func() (os.FileInfo, error)
}

func (m *mockFile) Close() error {
    return m.CloseFunc()
}

func (m *mockFile) Read(p []byte) (int, error) {
    return m.ReadFunc(p)
}

func (m *mockFile) ReadAt(p []byte, off int64) (int, error) {
    return m.ReadAtFunc(p, off)
}

func (m *mockFile) Seek(offset int64, whence int) (int64, error) {
    return m.SeekFunc(offset, whence)
}

func (m *mockFile) Stat() (os.FileInfo, error) {
    return m.StatFunc()
}

---

reader := ioutil.NopCloser(strings.NewReader("some string, replace with bytes etc"))

file := &mockFile{
    ReadFunc: func(p []byte) (int, error) {
        return reader.Read(p)
    },
    CloseFunc: func() error {
        return nil
    },
}

并通过模拟文件系统返回模拟文件:

fs := &mockFileSystem{
    OpenFunc: func(name string) (File, error) {
        return file, nil
    },
}