2016-05-12 1 views
2

Я пишу валидатор JSON в Go, и я хочу протестировать другой объект, который взаимодействует с моим Validator. Я использовал Validator как структуру с методами. Чтобы позволить мне вводить mock Validator в другой объект, я добавил интерфейс, который реализует Validator. Затем я заменил типы аргументов, чтобы ожидать интерфейса.Go: Должен ли я использовать интерфейс, позволяющий насмехаться?

// 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

Трудно сказать. Может быть, валидатор и другой объект должны войти в один пакет? – Volker

+0

, если вы действительно хотите тип 'validate-able', просто определите его. и показать некоторый код, который поможет нам понять более понятным. –

+0

Что не так с использованием типа интерфейса? Почему он не чувствует себя хорошо? Это дает вам большую гибкость. Вам будет разрешено изменять реализацию так, как вам удобно, без нарушения обратной совместимости. Все, что вам нужно позаботиться, - это реализовать интерфейс. – icza

ответ

3

Экспорт только ваш интерфейс, а не ваш конкретный тип. И добавьте конструктор 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()) за пределами его использования, и впрыскивать экземпляр, где они когда-либо использовать его. Это позволит им высмеивать интерфейс в своих тестах и ​​вводить макет.

Или потребитель может позаботиться об этом и просто напишет основной код, который является коротким и сладким, и выполняет свою работу.

2

ИМХО это хорошее решение. Интерфейсы дают вам больше свободы при тестировании и повторной реализации. Я часто использую интерфейсы, и я никогда не жалею об этом (особенно при тестировании), даже когда это была единственная реализация (которая, в моем случае, большая часть времени).

Вы можете быть заинтересованы в этом: http://relistan.com/writing-testable-apps-in-go/

Смежные вопросы