Как проверить вывод функции (stdout / stderr) в модульных тестах Go

у меня есть простая функция, которую я хочу проверить:

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        fmt.Print(message)
    }
}

но как я могу проверить, что функция фактически отправляет на стандартный вывод? Test:: Output делает то, что я хочу в Perl. Я знаю, что мог бы написать весь свой собственный шаблон, чтобы сделать то же самое в Go (как описано здесь):

orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
    t.Error("Failure!")
}

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

3 ответов


одна вещь, чтобы также помнить, нет ничего, что мешает вам писать функции, чтобы избежать шаблона.

например у меня есть приложение командной строки, которая использует log и я написал эту функцию:

func captureOutput(f func()) string {
    var buf bytes.Buffer
    log.SetOutput(&buf)
    f()
    log.SetOutput(os.Stderr)
    return buf.String()
}

затем использовал его следующим образом:

output := captureOutput(func() {
    client.RemoveCertificate("www.example.com")
})
assert.Equal("removed certificate www.example.com\n", output)

используя эту библиотеку assert:http://godoc.org/github.com/stretchr/testify/assert.


вы можете сделать одну из двух вещей. Первый-использовать примеры.

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

func ExampleHello() {
        fmt.Println("hello")
        // Output: hello
}

второй (и более подходящий, ИМО) - это используйте поддельные функции для вашего ввода-вывода. В вашем коде вы делаете:

var myPrint = fmt.Print

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        myPrint(message) // N.B.
    }
}

и в ваших тестах:

func init() {
    myPrint = fakePrint // fakePrint records everything it's supposed to print.
}

func Test...

другой вариант-использовать fmt.Fprintf С io.Writer что это os.Stdout в производственном коде, но можно сказать bytes.Buffer в тестах.


вы можете добавить оператор return в свою функцию, чтобы вернуть строку, которая фактически распечатана.

func (t *Thing) print(min_verbosity int, message string) string {
    if t.verbosity >= minv {
        fmt.Print(message)
        return message
    }
    return ""
}

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

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