Модульное тестирование синглтона, доступного как интерфейс - PullRequest
0 голосов
/ 21 апреля 2020

У меня есть одноэлементное приложение в моем приложении, но оно публикуется не как непосредственно структура, а как интерфейс (потому что я хочу иметь возможность динамически выбирать конкретную реализацию при инициализации одноэлементного режима). Вот код:

var once sync.Once
var instance defaultConfiguration

type Configuration interface {
    GetFoo() string
}

type defaultConfiguration struct {
}

func (dc defaultConfiguration) GetFoo() string {
    return "foo"
}

func NewConfiguration() Configuration {
    once.Do(func() {
        instance = defaultConfiguration{}
    })
    return instance
}

Затем я решил написать юнит-тест, который бы проверял, что NewConfiguration() будет фактически возвращать один и тот же экземпляр каждый раз:

func TestNewConfigurationSameInstance(t *testing.T) {
    configuration1 := NewConfiguration()
    configuration2 := NewConfiguration()
    if &configuration1 != &configuration2 {
        t.Error()
    }
}

Я думал, что это было бы целесообразно сравнить адреса возвращенных экземпляров, однако этот тест не пройден.

Тогда я подумал, ну, может быть, мне нужно вернуть указатель на экземпляр, поэтому я изменил код, чтобы посмотреть как это:

func NewConfiguration() *Configuration {
    once.Do(func() {
        instance = defaultConfiguration{}
    })
    return &instance
}

Но это даже не компилируется: происходит сбой с сообщением об ошибке

не может использовать & instance (type * defaultConfiguration) в качестве типа * Configuration в возвращаемом аргументе : * Конфигурация указатель на интерфейс, а не интерфейс

И я очень запутался. Почему я не могу вернуть указатель на интерфейс? Или почему возврат defaultConfiguration как Configuration допустим, а возврат *defaultConfiguration как *Configuration - нет?

И, в конце концов, каков надлежащий юнит-тест для моего варианта использования?

1 Ответ

0 голосов
/ 21 апреля 2020

Ваш код должен быть:

var once sync.Once
var instance *defaultConfiguration

type Configuration interface {
    GetFoo() string
}

type defaultConfiguration struct {
}

func (dc *defaultConfiguration) GetFoo() string {
    return "foo"
}

func NewConfiguration() Configuration {
    once.Do(func() {
        instance = &defaultConfiguration{}
    })
    return instance
}

Поскольку Configuration является интерфейсом, и вы хотите указатель на defaultConfiguration для его реализации.

Указатели на интерфейсы (например, *Configuration) редко нужны. Интерфейс уже является справочным значением, и указатель на некоторый тип прекрасно подходит для реализации интерфейса.

Для получения дополнительной информации по root проблеме прочитайте этот ответ или аналогичные ресурсы .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...