我正在Go中编写一个JSON验证器,我想测试另一个与我的Validator交互的对象。我已经使用方法将Validator实现为struct。为了允许我将模拟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
}
这很有效,但感觉不太对劲。合作者在生产代码中看不到我的具体类型;我只介绍了适合测试的界面。如果我到处都这样做,我觉得我会通过为只有一个真正实现的方法编写接口来重复自己。
有更好的方法吗?
答案 0 :(得分:3)
更新:我撤回了之前的回答。不要跨包导出接口。使您的func返回具体类型,以便允许使用者创建自己的界面并在需要时覆盖。
请参阅:https://github.com/golang/go/wiki/CodeReviewComments#interfaces HatTip:@rocketspacer
我通常也会将我的测试编码在与我的包代码不同的包中。这样,我只能看到我导出的内容(如果导出太多,你有时会看到混乱的东西)。
遵循本指南,测试您的包裹,过程如下:
仅导出您的界面,而不是您的具体类型。并添加一个New()
构造函数,以便人们可以从包中实例化一个符合界面的默认实例。
package validator
type Validator interface {
Validate(doc interface{}) (valid bool, err error)
ValidateString(doc string) (valid bool, err error)
}
func New() Validator {
return &validator{}
}
type validator struct {
schemaLoader gojsonschema.JSONLoader
validationError error
}
func (v *validator) Validate(doc interface{}) (valid bool, err error) {
...
}
func (v *validator) ValidateString(doc string) (valid bool, err error) {
...
}
这样可以保持您的API包清洁,只导出Validator
和New()
。
您的消费者只需要了解界面。
package main
import "foo.com/bar/validator"
func main() {
v := validator.New()
valid, err := v.Validate(...)
...
}
这使您的消费者能够遵循依赖注入模式并在其使用之外实例化(调用New()
),并将实例注入他们使用它的地方。这将允许他们在测试中模拟接口并注入模拟。
或者,消费者可以更少关心,只需编写上面简短而甜蜜的主要代码并完成工作。
答案 1 :(得分:2)
恕我直言,这是一个很好的解决方案。接口在测试和重新实现时为您提供更多自由。我经常使用接口而且我从不后悔(特别是在测试时),即使它是单个实现(在我的情况下,大部分时间都是这样)。