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 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?