Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Testing 如何在go中模拟对ioutil.ReadFile的调用?_Testing_Go - Fatal编程技术网

Testing 如何在go中模拟对ioutil.ReadFile的调用?

Testing 如何在go中模拟对ioutil.ReadFile的调用?,testing,go,Testing,Go,我有以下功能: func ObtainTranslationStringsFile(path string) ([]string, error) { if contents, err := ioutil.ReadFile(path); err != nil { return ObtainTranslationStrings(string(contents)) } else { return nil, err } } 我需要模拟ioutil

我有以下功能:

func ObtainTranslationStringsFile(path string) ([]string, error) {
    if contents, err := ioutil.ReadFile(path); err != nil {
        return ObtainTranslationStrings(string(contents))
    } else {
        return nil, err
    }
}

我需要模拟ioutil.ReadFile,但我不知道如何做。有可能吗?

不要模仿
ioutil.ReadFile
。一旦您模拟了它,测试
obtaintransationstringsfile
就不会像测试
obtaintransationstrings
那样进行任何测试。如果需要测试
obtaintransationstringsfile
,请创建一个临时文件并将其路径传递到
obtaintransationstringsfile
。这样,您将实际测试由该函数添加的功能。

如果您想模拟此功能,有几种方法可以处理此问题。第一个,也许是最简单的,是从使用改为使用io.Reader接口的调用。然后很容易按照注入自己的io.Reader/filesystem实现

如果您不希望更改方法签名,另一个选项是不使用
ioutil
,而是为ReadFile声明函数替换。使用对象方法而不是函数注入这一点会更容易,但这是可以做到的。它仅仅依赖于包级别的变量魔术,这可能会让一些人感到厌恶。请参阅下面探讨的选项:

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    _ "log"
)

type ReadFile func(filename string) ([]byte, error)
// myReadFile is a function variable that can be reassigned to handle mocking in option #2
var myReadFile = ioutil.ReadFile

type FakeReadFiler struct {
    Str string
}

// here's a fake ReadFile method that matches the signature of ioutil.ReadFile
func (f FakeReadFiler) ReadFile(filename string) ([]byte, error) {
    buf := bytes.NewBufferString(f.Str)
    return ioutil.ReadAll(buf)
}

func main() {
    payload := "PAYLOAD"
    path := "/dev/nul"
    buf := bytes.NewBufferString(payload)

    // option 1 is more elegant, but you have to change the method signature to take an io.Reader
    result1, err := ObtainTranslationStringsFileChoice1(buf)
    fmt.Printf("ObtainTranslationStringsFileChoice1 == %#v, %#v\n", result1, err)

    // option 2 keeps the method signature, but allows you to reassign a the myReadFile variable to change the method behavior for testing
    fake := FakeReadFiler{Str: payload}
    myReadFile = fake.ReadFile
    result2, err := ObtainTranslationStringsFileChoice2(path)
    fmt.Printf("ObtainTranslationStringsFileChoice2 == %#v, %#v\n", result2, err)
}

func ObtainTranslationStringsFileChoice1(rdr io.Reader) ([]string, error) {
    if contents, err := ioutil.ReadAll(rdr); err == nil {
        return []string{string(contents)}, nil
    } else {
        return nil, err
    }
}

func ObtainTranslationStringsFileChoice2(path string) ([]string, error) {
    if contents, err := myReadFile(path); err == nil {
        return []string{string(contents)}, nil
    } else {
        return nil, err
    }
}
此处为游乐场版本:


如果您想变得更复杂,我建议您制作一个完整的文件系统模拟。这是我通常做的,并不像听起来那么难。在您的示例中,您没有使用结构和接口,这确实使这种模拟更加健壮。

以防您需要模拟io

在你的包裹里申报

type ioReader interface {
    io.Reader
}
这只需要告诉mockry您需要这样的接口,它将生成相应的mock

然后生成模拟

go get github.com/vektra/mockery/.../
mockery -inpkg -all
然后在测试代码中,您可以这样做

str := "some string"
r := &mockIoReader{}
r.
    On("Read", mock.Anything).
    Run(func(args mock.Arguments) {
        bytes := args[0].([]byte)
        copy(bytes[:], str)
    }).
    Return(len(str), nil)

Go习惯用法是使用一个guard子句并在if的第一个分支中返回错误(如果有),避免使用
else
。这将使倒置的if条件更加清晰,无需测试。我不同意。对
obtaintransationstringsfile
的测试不应该尝试exercise
ioutil.ReadFile
,它应该练习如何处理
路径
内容
错误
,以及返回值。因此,
ioutil.ReadFile
可以而且应该被模拟出来。(并且仅在集成测试级别执行)不需要执行“real ioutils.ReadFile”函数来执行单元测试。函数obtaintransactionstringsfile的单元测试应该只测试函数内部的逻辑,而不是它调用的函数。因此,如果有办法的话,最好是模拟ioutils.ReadFile来返回错误,而不是返回错误。“测试
obtaintransationstringsfile
不会像测试
obtaintransationstrings
那样进行测试。”。前者的逻辑在后者中并不存在,也就是说,当
err==nil
vs.
err!=无
。这一逻辑需要自己的检验。
obtaintransationstringsfile
的单元测试不应断言/激发来自
ioutil.ReadFile
的“正确”行为。这不是他们的责任。他们的工作是断言
obtaintransationstringsfile
的正确行为,以响应(模拟的)
ioutil.ReadFile
。当然,集成测试是另一回事…