Go中有一个基本函数,它打开一个文件并尝试解码其JSON内容。
我正在尝试提取默认的json.NewDecoder()
函数,这样我就可以在测试中轻松地模拟它。
然而,我的实现似乎返回了一个错误:
不能使用json。NewDecoder(类型func(io.Reader)*json.Doder)作为NewConfig 参数中的类型decoderFactory
代码:
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: %vn", err)
}
}
这是Go游乐场的链接
我哪里错了?
json.NewDecoder()
是一个具有以下声明的函数:
func NewDecoder(r io.Reader) *Decoder
其返回类型为*json.Decoder
。json.Decoder
不是一个接口,而是一个具体的类型。如果返回类型不同,则有两种函数类型不同:Spec:函数类型:
函数类型表示具有相同参数和结果类型的所有函数的集合。
因此,您不能构造一个返回接口的新类型,并期望它与json.NewDecoder
相同,或者它将接受值json.NewDecoder
。
但"看似"的简单解决方案是:将decoderFactory
定义为一种函数类型,与json.NewDecoder
完全一样:
type decoderFactory func(r io.Reader) *json.Decoder
这是编译的,好吧…但现在如何模拟?
现在该怎么嘲笑
当然,在这种形式中,您将失去模拟json.NewDecoder()
的可能性(因为"mocker"必须返回类型为*json.Decoder
的值,并且不接受其他值)。那该怎么办?
你必须使用不同的工厂类型。工厂类型应该是一个返回接口的函数(你可以提供不同的实现),你走在了正确的轨道上:
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: %vn", err)
}
这是有效的并且可以编译,因为jsonDecoderFact
与decoderFactory
具有完全相同的类型。
如果您想使用不同的实现进行测试/模拟:
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: %vn", err2)
}
试试围棋场上的例子。