Как реализовать или имитировать интерфейс Go с отражением?

Я хочу реализовать интерфейсы Go с отражением для создания макетов и заглушек. Но если я посмотрю на пакет reflect, я не пойму, как это сделать (возможно, это не так). возможный).

Пример: Проверка того, что функция вызывает WriteHeader(404) на ResponseWriter:

type ResponseWriterMock struct {                              //
  status int                                                  //
}                                                             // How to replace
func (*ResponseWriterMock) Header() Header {}                 // this block with 
func (*ResponseWriterMock) Write([]byte) (i int, e error) {}  // a reflectivly 
func (m *ResponseWriterMock) WriteHeader(status int) {        // generated mock? 
  m.status = status                                           //  
}                                                             //
responseWriterMock := new(ResponseWriterMock)

funcToTest(responseWriterMock)

if responseWriterMock.status != 404 {
    // report error
}

С RhinoMocks (C#) я бы написал так:

var responseWriterMock = MockRepository.GenerateMock<ResponseWriter>();

funcToTest(responseWriterMock);

responseWriterMock.AssertWasCalled(rw => rw.WriteHeader(404));

Как я могу реализовать интерфейсы Go с отражением?

Дополнение

Похоже, сегодня это невозможно.


person deamon    schedule 18.10.2012    source источник
comment
Пожалуйста, добавьте некоторый, предположительно даже недействительный, код Go, показывающий, чего вы хотите достичь. Я, например, очень слабо представляю, о чем вы просите. И да, я подозреваю, что вам не нужно отражение для этого ;-)   -  person zzzz    schedule 18.10.2012
comment
Я добавил пример, и, как вы видите, мне нужно реализовать функции только для насмешек и создать структуру.   -  person deamon    schedule 18.10.2012


Ответы (2)


Насколько мне известно, и ответы связаны с этим вопрос, невозможно создавать новые типы во время выполнения.

Вы можете попробовать пакет go-eval, который должен поддерживать определение новых типов в своей вселенной.

person nemo    schedule 18.10.2012

К сожалению, из-за статической природы языка пакет отражения немного ограничен, и нет возможности создавать динамические объекты во время выполнения. Возможно, это никогда не будет возможно.

Я могу предложить несколько решений - все они связаны с созданием любых заглушек/моков во время разработки.

  1. Пишите свои моки вручную.

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

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

  2. Используйте набор инструментов testify.

    На момент написания статьи это был самый популярный проект, предоставляющий поддержку мокапов. Однако, поскольку проект предоставляет другие функции, сомнительно, сколько лайков связано с самой возможностью имитации.

    Я не использовал этот инструментарий и не могу предоставить подробный анализ того, хорош ли он.

    Глядя на README, кажется, что он использует подход, очень похожий на ручной макет (объясненный выше). Он предоставляет некоторый вспомогательный код сверху, но поддельные методы по-прежнему необходимо вводить вручную. Затем заглушка в тестах выполняется путем указания метода через строку, что противоречит статической природе языка Go и может быть проблемой для некоторых пользователей.

  3. Используйте официальный инструмент mock от Go.

    Этот инструмент, похоже, приобрел популярность с тех пор, как я последний раз писал о нем. Кроме того, как и в официальном проекте Go, можно ожидать, что этот инструмент быстро примет любые новые функции Go. Поддержка модулей go — хороший пример того, как другие инструменты отстают.

    Это требует особого подхода к насмешкам. Вам необходимо указать ваши тестовые ожидания в начале тестового кода. В конце теста фреймворк проверяет, оправдались ли все ожидания. Неожиданные вызовы приводят к тому, что фреймворк не проходит тест.

    Если вы используете специальную среду тестирования, такую ​​как Ginkgo, может возникнуть проблема с чистой интеграцией. В своих проектах я написал вспомогательные функции, чтобы восполнить пробел.

    Хотя он генерирует для вас поддельные методы, генерируемые им методы утверждений и ожиданий принимают interface{} типы. Это позволяет платформе предоставлять более продвинутые средства сопоставления аргументов, но также означает, что вам необходимо убедиться, что вы передаете правильное количество аргументов и в правильном порядке.

    По моему опыту, тесты, написанные с помощью этого инструмента, имеют тенденцию быть больше по размеру и временами более сложными для чтения. С положительной стороны, они часто более тщательно проверяют вещи и более выразительны.

  4. Используйте инструмент подделки.

    Этот инструмент часто используется в экосистеме Cloud Foundry и зарекомендовал себя как жизнеспособный вариант.

    Он позволяет вам отслеживать количество вызовов данного метода, аргументы, которые использовались для вызова метода, и подделать либо весь метод с помощью пользовательской реализации, либо указать возвращаемые значения результата.

    Что делает его хорошим выбором, так это то, что он производит очень явные подделки. Разработчикам не нужно использовать строковые имена для указания метода, а также им не нужно запоминать порядок и количество аргументов. Сгенерированные вспомогательные методы точно имитируют исходные с аргументами правильного типа и порядка.


Некоторые из опций, которые я предоставил выше, требуют, чтобы вы запускали инструмент командной строки для создания исходного кода для ваших макетов. Поскольку количество интерфейсов, которые необходимо смоделировать, может быстро расти, есть классный трюк, который вы можете использовать, чтобы отслеживать все.

Вы можете воспользоваться функциональностью go:generate в Go, чтобы легко генерировать все ваши заглушки/подделки без необходимости вызывать какие-либо параметры команды или вручную запускать инструмент для каждого интерфейса.

Все, что вам нужно сделать, это добавить правильный комментарий go:generate в файл, содержащий ваш интерфейс.

насмешка

//go:generate mockgen -source person.go

type Person interface {
  Name() string
  Age() int
}

подделка

//go:generate counterfeiter ./ Person

type Person interface {
  Name() string
  Age() int
}

Затем вы можете запустить команду go generate ./... в корне вашего проекта, и все заглушки/подделки будут сгенерированы для вас заново. Это может быть спасением, если вы работаете над проектом с несколькими соавторами.

person Momchil Atanasov    schedule 22.07.2015
comment
Совет go:generate очень полезен. Спасибо! - person Ryan Walls; 21.04.2017