为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
繁花如伊 2022-01-10 15:04:28
我正在用 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. Alwaysfunc (val *PassingValidator) Validate(doc interface{}) (valid bool, err error) {    return true, nil}// ValidateString passes. Alwaysfunc (val *PassingValidator) ValidateString(doc string) (valid bool, err error) {    return true, nil}这行得通,但感觉不太对劲。除了我在生产代码中的具体类型之外,协作者不会看到任何东西;我只介绍了适合测试的接口。如果我到处都这样做,我觉得我会重复自己,为只有一个真正实现的方法编写接口。有一个更好的方法吗?
查看完整描述

2 回答

?
阿晨1998

TA贡献2037条经验 获得超6个赞

更新:我收回我之前的回答。不要跨包导出接口。让您的函数返回具体类型,以便允许消费者创建自己的接口并根据需要进行覆盖。

请参阅:https ://github.com/golang/go/wiki/CodeReviewComments#interfaces HatTip:@rocketspacer

我通常也将我的测试编码在与我的包代码不同的包中。这样,我只能看到我导出的内容(如果导出太多,你有时会看到你弄乱了什么)。

按照本指南,为了测试您的包,过程将是:

  • 使用 func 像往常一样创建复杂对象

  • 根据需要在内部使用接口(例如,Car <- Object -> House)

  • 只导出你的混凝土,而不是接口

  • 在测试期间,指定一个测试方法,该方法采用您的具体方法的测试接口,并根据需要更改您的接口。您在测试包中创建此测试接口。

下面的原始答案供后代使用


仅导出您的界面,而不是您的具体类型。并添加一个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()在其使用之外实例化(调用),并将实例注入到他们使用它的任何地方。这将允许他们在测试中模拟接口并注入模拟。


或者,消费者可以不那么关心,只写上面的主要代码,简短而甜美并完成工作。


查看完整回答
反对 回复 2022-01-10
?
慕神8447489

TA贡献1780条经验 获得超1个赞

恕我直言,这是一个很好的解决方案。接口在测试和重新实现时为您提供更多自由。我经常使用接口,我从不后悔(尤其是在测试时),即使它是一个单一的实现(在我的情况下,大部分时间)。

您可能对此感兴趣:http : //relistan.com/writing-testable-apps-in-go/


查看完整回答
反对 回复 2022-01-10
  • 2 回答
  • 0 关注
  • 147 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信