猿问

我应该使用接口来允许模拟吗?

我正在用 Go 编写一个 JSON 验证器,我想测试另一个与我的验证器交互的对象。我已经将 Validator 实现为带有方法的结构。为了允许我将一个模拟 Validator 注入另一个对象,我添加了一个 Validator 实现的接口。然后我交换了参数类型以期望接口。


// Validator validates JSON documents.

type Validator interface {

    // Validate validates a decoded JSON document.

    Validate(doc interface{}) (valid bool, err error)


    // ValidateString validates a JSON string.

    ValidateString(doc string) (valid bool, err error)

}


// SchemaValidator is a JSON validator fixed with a given schema.

// This effectively allows us to partially apply the gojsonschema.Validate()

// function with the schema.

type SchemaValidator struct {

    // This loader defines the schema to be used.

    schemaLoader    gojsonschema.JSONLoader

    validationError error

}


// Validate validates the given document against the schema.

func (val *SchemaValidator) Validate(doc interface{}) (valid bool, err error) {

    documentLoader := gojsonschema.NewGoLoader(doc)

    return val.validate(documentLoader)

}


// ValidateString validates the given string document against the schema.

func (val *SchemaValidator) ValidateString(doc string) (valid bool, err error) {

    documentLoader := gojsonschema.NewStringLoader(doc)

    return val.validate(documentLoader)

}

我的一个模拟看起来像这样:


// PassingValidator passes for everything.

type PassingValidator bool


// Validate passes. Always

func (val *PassingValidator) Validate(doc interface{}) (valid bool, err error) {

    return true, nil

}


// ValidateString passes. Always

func (val *PassingValidator) ValidateString(doc string) (valid bool, err error) {

    return true, nil

}

这行得通,但感觉不太对劲。除了我在生产代码中的具体类型之外,协作者不会看到任何东西;我只介绍了适合测试的接口。如果我到处都这样做,我觉得我会重复自己,为只有一个真正实现的方法编写接口。


有一个更好的方法吗?


繁花如伊
浏览 148回答 2
2回答

阿晨1998

更新:我收回我之前的回答。不要跨包导出接口。让您的函数返回具体类型,以便允许消费者创建自己的接口并根据需要进行覆盖。请参阅:https&nbsp;://github.com/golang/go/wiki/CodeReviewComments#interfaces HatTip:@rocketspacer我通常也将我的测试编码在与我的包代码不同的包中。这样,我只能看到我导出的内容(如果导出太多,你有时会看到你弄乱了什么)。按照本指南,为了测试您的包,过程将是:使用 func 像往常一样创建复杂对象根据需要在内部使用接口(例如,Car <- Object -> House)只导出你的混凝土,而不是接口在测试期间,指定一个测试方法,该方法采用您的具体方法的测试接口,并根据需要更改您的接口。您在测试包中创建此测试接口。下面的原始答案供后代使用仅导出您的界面,而不是您的具体类型。并添加一个New()构造函数,以便人们可以从您的包中实例化一个符合接口的默认实例。package validatortype Validator interface {&nbsp; &nbsp; Validate(doc interface{}) (valid bool, err error)&nbsp; &nbsp; ValidateString(doc string) (valid bool, err error)}func New() Validator {&nbsp; &nbsp; return &validator{}}type validator struct {&nbsp; &nbsp; schemaLoader&nbsp; &nbsp; gojsonschema.JSONLoader&nbsp; &nbsp; validationError error}func (v *validator) Validate(doc interface{}) (valid bool, err error) {&nbsp; &nbsp; ...}func (v *validator) ValidateString(doc string) (valid bool, err error) {&nbsp; &nbsp; ...}这使您的 API 包保持干净,仅包含Validator和New()导出。您的消费者只需要了解界面。package mainimport "foo.com/bar/validator"func main() {&nbsp; &nbsp; v := validator.New()&nbsp; &nbsp; valid, err := v.Validate(...)&nbsp; &nbsp; ...}这让您的消费者可以遵循依赖注入模式并New()在其使用之外实例化(调用),并将实例注入到他们使用它的任何地方。这将允许他们在测试中模拟接口并注入模拟。或者,消费者可以不那么关心,只写上面的主要代码,简短而甜美并完成工作。

慕神8447489

恕我直言,这是一个很好的解决方案。接口在测试和重新实现时为您提供更多自由。我经常使用接口,我从不后悔(尤其是在测试时),即使它是一个单一的实现(在我的情况下,大部分时间)。您可能对此感兴趣:http&nbsp;:&nbsp;//relistan.com/writing-testable-apps-in-go/
随时随地看视频慕课网APP

相关分类

Go
我要回答