Допустим, у вас есть следующее
type Person interface {
GetFullName() string
PrintName()
}
type Banker struct {
FirstName string
LastName string
}
func (b Banker) GetFullName() string {
return fmt.Sprintf("%s %s", b.FirstName, b.LastName)
}
func (b Banker) PrintName() {
return log.Println(b.GetFullName())
}
Как вы издеваетесь над GetFullName из PrintName?
Обратите внимание: это всего лишь пример, призванный облегчить понимание основной проблемы (имитационная функция приемника, используемая в функции приемника).
Вы не высмеиваете эти вещи.
Название просто неправильное. Вы не спрашиваете, как провести модульное тестирование функции-приемника, которая вызывает другую функцию-приемник, вы спрашиваете, как смоделировать функцию-приемник. Между возможностью что-то протестировать и над чем-то издеваться нет абсолютно никакой прямой связи, издевательств следует избегать, насколько это возможно.
Оставляя в стороне вопрос о том, следует ли вам это делать, один из способов — сделать функцию-приемник, которую вы хотите имитировать, оболочкой вокруг функциональной переменной, которую можно заменить в тестах:
var bankerGetFullName = func(Banker) string {
return fmt.Sprintf("%s %s", b.FirstName, b.LastName)
}
type Person interface {
GetFullName() string
PrintName()
}
type Banker struct {
FirstName string
LastName string
}
func (b Banker) GetFullName() string {
return bankerGetFullName(b)
}
func (b Banker) PrintName() {
return log.Println(b.GetFullName())
}
Оболочка функции не экспортируется, поэтому ее нельзя изменить кодом вне пакета.
Затем, предполагая, что ваши тесты находятся в том же пакете, что и тестируемый код (что, по мнению некоторых, само по себе нежелательно), ваш тест может заменить реализацию оболочки, чтобы предоставить макет по мере необходимости:
// ARRANGE
og := bankerGetFullName
defer func() { bankerGetFullName = og }()
bankerGetFullName = func(Banker) string { return "mock result" }
sut := Banker{}
// ACT
result := sut.Print()
// ASSERT
... test the log output to verify that "mock result" was printed ...
Как я уже сказал, это не рекомендация, что вам следует это делать, а лишь прямой ответ на вопрос, как это можно или можно сделать.
Вы неправильно понимаете модульное тестирование. Вы только издеваетесь над соавторами, а в этом примере их нет.
Вы хотите протестировать поведение устройства, которое является вашим Banker
. Если вы выполните рефакторинг PrintName
, чтобы он был:
func (b Banker) PrintName() {
log.Println(b.FirstName + " " + b.LastName)
}
ваша программа по-прежнему корректна.
Если вы хотите протестировать метод, который вызывает другие методы, один из способов сделать это — настроить структуру так, чтобы методы, от которых зависит тестируемый метод, возвращали контролируемые значения. Вы не можете «переопределить» методы, чтобы издеваться над ними.