doucha5080 2016-07-26 13:20
浏览 84
已采纳

Golang返回指向接口的指针引发错误

I have a basic function in Go that opens a file and tries to decode its JSON contents.

I am trying to extract the default json.NewDecoder() function so I can easily mock this in my tests.

However, my implementation seems to return an error:

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

Code:

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
", err)
    }
}

Here's a link to the Go playground

Where am I going wrong?

  • 写回答

1条回答 默认 最新

  • duanjuete9206 2016-07-26 13:27
    关注

    The json.NewDecoder() is a function with the following declaration:

    func NewDecoder(r io.Reader) *Decoder
    

    Its return type is *json.Decoder. json.Decoder is not an interface, it's a concrete type. And 2 function types are different if their return type is different: Spec: Function types:

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

    So you can't construct a new type returning an interface, and expect to be the same as json.NewDecoder, or that it'll accept the value json.NewDecoder.

    But the "seemingly" easy fix is: define your decoderFactory to be a function type exactly what json.NewDecoder is:

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

    This compiles, ok... but how to mock now?

    How to mock now?

    Of course in this form, you'll lose the possibility to mock json.NewDecoder() (because a "mocker" would have to return a value of type *json.Decoder and nothing else would be accepted). What to do then?

    You have to use a different factory type. The factory type should be a function which returns an interface (of which you can provide different implementations), you were on the right track:

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

    But you can't use json.NewEncoder as-is to pass as a value of decoderFactory. But fear not, it is very easy to create a function of type decoderFactory which will call json.NewEncoder() under the hood:

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

    We're mocking the behaviour of json.Decoder, and not the json.NewDecoder() factory function.

    Using this jsonDecoderFact():

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

    This is valid and compiles, because jsonDecoderFact has exactly the same type as decoderFactory.

    If you want to test / mock with a different implementation:

    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}
    }
    

    Using it:

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

    Try the examples on the Go Playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 使用MATLAB进行余弦相似度计算加速
  • ¥15 服务器安装php5.6版本
  • ¥15 我想用51单片机和数码管做一个从0开始的计数表 我写了一串代码 但是放到单片机里面数码管只闪烁一下然后熄灭
  • ¥20 系统工程中,状态空间模型中状态方程的应用。请猛男来完整讲一下下面所有问题
  • ¥15 我想在WPF的Model Code中获取ViewModel Code中的一个参数
  • ¥15 arcgis处理土地利用道路 建筑 林地分类
  • ¥20 使用visual studio 工具用C++语音,调用openslsx库读取excel文件的sheet问题
  • ¥100 寻会做云闪付tn转h5支付链接的技术
  • ¥15 DockerSwarm跨节点无法访问问题
  • ¥15 使用dify通过OpenAI 的API keys添加OpenAI模型时报了“Connection Error”错误