pointers - Golang返回指向接口(interface)的指针抛出错误

标签 pointers testing go interface mocking

我在 Go 中有一个基本函数,它打开一个文件并尝试解码其 JSON 内容。

我正在尝试提取默认的 json.NewDecoder() 函数,这样我就可以在我的测试中轻松地模拟它。

但是,我的实现似乎返回了一个错误:

cannot use json.NewDecoder (type func(io.Reader) *json.Decoder) as type decoderFactory in argument to NewConfig

代码:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "os"
)

type openFile func(name string) (*os.File, error)

type decoderFactory func(r io.Reader) decoder

type decoder interface {
    Decode(v interface{}) error
}

type Config struct {
    ConsumerKey,
    ConsumerSecret,
    AccessToken,
    AccessTokenSecret string
}

func NewConfig(open openFile, d decoderFactory) (*Config, error) {
    c := new(Config)
    file, err := open("some.file")
    if err != nil {
        return nil, fmt.Errorf("error opening config file")
    }
    defer file.Close()

    decoder := d(file)
    if err := decoder.Decode(&c); err != nil {
        return nil, fmt.Errorf("error decoding config JSON")
    }

    return c, nil
}

func main() {
    _, err := NewConfig(os.Open, json.NewDecoder)
    if err != nil {
        fmt.Fprintf(os.Stderr, "something bad happened: %v\n", err)
    }
}

这是 Go playground 的链接

我哪里错了?

最佳答案

json.NewDecoder()是具有以下声明的函数:

func NewDecoder(r io.Reader) *Decoder

它的返回类型是*json.Decoder . json.Decoder 不是接口(interface),它是具体类型。如果返回类型不同,则 2 个函数类型不同:Spec: Function types:

A function type denotes the set of all functions with the same parameter and result types.

所以你不能构造一个返回接口(interface)的新类型,并期望与 json.NewDecoder 相同,或者它会接受值 json.NewDecoder.

“看似”的简单解决方法是:将您的 decoderFactory 定义为一个函数类型,与 json.NewDecoder 完全相同:

type decoderFactory func(r io.Reader) *json.Decoder

这可以编译,好的......但是现在如何模拟?

现在如何模拟?

当然,在这种形式下,您将失去模拟 json.NewDecoder() 的可能性(因为“模拟者”必须返回类型为 *json.Decoder 的值 其他任何东西都不会被接受)。那怎么办呢?

您必须使用不同的工厂类型。工厂类型应该是一个返回接口(interface)的函数(你可以提供不同的实现),你是在正确的轨道上:

type MyDecoder interface {
    Decode(v interface{}) error
    // List other methods that you need from json.Decoder
}

type decoderFactory func(r io.Reader) MyDecoder

但是您不能使用 json.NewEncoder 原样 作为 decoderFactory 的值传递。但不要害怕,创建一个 decoderFactory 类型的函数非常容易,它会在后台调用 json.NewEncoder():

func jsonDecoderFact(r io.Reader) MyDecoder {
    return json.NewDecoder(r)
}

我们正在模拟 json.Decoder 的行为,而不是 json.NewDecoder() 工厂函数。

使用这个 jsonDecoderFact():

_, err := NewConfig(os.Open, jsonDecoderFact)
if err != nil {
    fmt.Fprintf(os.Stderr, "something bad happened: %v\n", err)
}

这是有效的并且可以编译,因为 jsonDecoderFactdecoderFactory 具有完全相同的类型。

如果你想用不同的实现来测试/模拟:

type TestDecoder struct {
    r io.Reader
}

func (t TestDecoder) Decode(v interface{}) error {
    // Test / mocking logic here
    return nil
}

func testDecoderFact(r io.Reader) MyDecoder {
    return TestDecoder{r}
}

使用它:

_, err2 := NewConfig(os.Open, testDecoderFact)
if err2 != nil {
    fmt.Fprintf(os.Stderr, "something bad happened: %v\n", err2)
}

试试 Go Playground 上的例子.

关于pointers - Golang返回指向接口(interface)的指针抛出错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38591020/

相关文章:

go - 为什么在 golang 中不断逃逸到堆中?

我可以释放最初从另一个指针分配的内存吗?

c - C 中的指针问题,找不到我的错误

python - 何时使用 TestClass 实例变量与 Pytest Fixtures

Laravel Dusk 忽略 .env.dusk 和 .env.dusk.local(使用 Valet 和 Laravel 5.5)

go - 您如何反射(reflection)地调用Go结构的方法?

go - 为什么golang时间功能在某些日期会失败

c++ - sizeof字符指针混淆

c - C 中的越界地址/指针

unit-testing - 测试中的整个单轨操作调用