Автор статьи: Рустем Галиев
IBM Senior DevOps Engineer & Integration Architect. Официальный DevOps ментор и коуч в IBM
Привет Хабр!
Иногда мне приходится расширять функционал наших платформ по запросам разработчиков и в для этого мне отлично подошел Go. Однако нужно не только уметь писать на языке, но и уметь его тестировать. Как вы поняли, сегодня я хотел бы рассказать про основы тестирования в Go.
Тестирование — это первостепенная задача в Go. Пакет тестирования предоставляет API для написания тестового кода. Опишу упражнение, в котором мы реализуем и выполним базовые тестовые случаи с помощью стандартного пакета тестирования. Выполним их с помощью набора инструментов Go.
Шаг первый:
Внедрим тесты с помощью стандартного пакета тестирования.
Выполним тесты из командной строки с помощью команды
go test.
Основы тестирования
Для многих языков программирования тестирование является дополнительной функцией. Это не относится к Go. Пакет тестирования встроен в среду выполнения Go и предлагает все необходимое для написания сложного тестового кода. API прост для понимания и в то же время позволяет избежать синтаксического сахара.
Тестовый код находится в файле исходного кода, отличном от рабочего исходного кода. По конвенции эти исходные файлы имеют файл, заканчивающийся на _test.go
. Например, для файла Go http.go
вы должны создать тестовый файл с именем http_test.go
. Нет специальной директории для тестового кода. Файл просто живет вместе с производственным исходным кодом.
При написании тестового кода необходимо учитывать всего несколько шагов:
В список импорта необходимо добавить пакет тестирования.
Каждый тестовый пример представлен функцией.
Функция должна быть экспортирована, а ее имя должно начинаться с префикса Test.
Функция имеет единственный параметр, который ссылается на тип testing.T. Параметры предоставляют вам доступ к соответствующему API для реализации утверждений.
Приступим. Сперва создадим файл hello.go со следующим содержимым
package main
import "errors"
func Hello(name string, language string) (string, error) {
if name == "" {
name = "World"
}
prefix := ""
switch language {
case "english":
prefix = "Hello"
case "spanish":
prefix = "Hola"
case "german":
prefix = "Hallo"
default:
return "", errors.New("need to provide a supported language")
}
return prefix + " " + name, nil
}
Экспортируемая функция Hello принимает два параметра. Параметра “name” представляет имя человека, а параметр “language” определяет языковой префикс, используемый для приветствия человека. Например, для параметров «Ben» и «English» в возвращаемой строке будет «Hello, Ben». Логика может вернуть ошибку, если язык не поддерживается.
Давайте напишем тестовый пример для этого самого варианта использования в файле с именем hello_test.go
. Как вы можете видеть в приведенном ниже примере кода, мы вызываем исходный код (в данном случае функцию Hello) и затем проверяем его результат. Если результат не соответствует вашим ожиданиям, вы можете отметить это с помощью вызова функции t.Error*
. Вызов t.Error*
сообщает об ошибке теста, но продолжает выполнение набора тестов до тех пор, пока не будут выполнены все тестовые кейсы:
package main
import "testing"
func TestEnglish(t *testing.T) {
name := "Ben"
language := "english"
expected := "Hello Ben"
actual, err := Hello(name, language)
if err != nil {
t.Errorf("Should not produce an error")
}
if expected != actual {
t.Errorf("Result was incorrect, got: %s, want: %s.", actual, expected)
}
}
Вероятно, вы можете представить еще много тестовых случаев для функции Hello. Не стесняйтесь добавлять их, чтобы получить больше опыта
Но теперь прогоним наши тесты
Возможность выполнять тесты связана с тулчейном Go. Чтобы выполнить тесты, просто запустим исполняемый файл go с помощью команды test
. В качестве последнего параметра команды нам нужно будет указать пакеты, которые вы хотите протестировать. Вместо предоставления конкретного пакета я часто просто рекурсивно выполняю все тесты из корневой директории. Полная команда для такого поведения — go test ./….
Мы имеем дело только с основным пакетом, который содержит один тест. Выполним тесты с помощью следующей команды. Также мы используем параметр -v
для отображения подробного вывода. Все тесты должны пройти:go test -v
Отлично, идем дальше, задействуем Testify.
Стандартный пакет тестирования предоставляет API для написания тестового кода. Однако API не хватает, когда дело доходит до предоставления удобных методов и синтаксического сахара. Testify — это пакет с открытым исходным кодом, упрощающий написание логики утверждений. Сейчас мы реализуем и выполним базовые тестовые примеры с помощью testify.
Сперва добавим testify с помощью команды go get
:go get github.com/stretchr/testify
Следующий код (также hello_test.go
) импортирует пакет assert из файла testify. Его API предлагает множество удобных методов, которые упрощают написание утверждений. Здесь мы проверяем нулевое значение ошибки с помощью assert.Nil
и равенство фактического с ожидаемым возвращаемым значением через assert.Equal
:
package main
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestEnglish(t *testing.T) {
name := "Ben"
language := "english"
expected := "Hello Ben"
actual, err := Hello(name, language)
assert.Nil(t, err)
assert.Equal(t, expected, actual)
}
И запустим тест: go test -v
Ну и перейдем к созданию метрик покрытия кода и репортов.
Метрики покрытия кода помогают определить части рабочего исходного кода, которые были покрыты тестами. Наличие хорошего охвата правильными утверждениями означает, что вы можете уверенно проводить рефакторинг кода или внедрять новые функции и исправления ошибок, не нарушая существующие функции. Сейчас мы создадим метрики покрытия кода и отобразим их в браузере.
Немного теории
Показатели покрытия кода отвечают на вопрос: «Какая часть нашего кода была проверена тестами?» В большинстве случаев нереально стремиться к 100% охвату, поскольку вы потратите много усилий на написание тестов. Более практичный подход — стремиться к 70–75% охвата. Этот процент дает вам достаточную уверенность, чтобы вносить изменения в рабочий исходный код, не ломая его немедленно. Наиболее часто используемые критерии покрытия кода следующие:
Покрытие операторов (также известное как покрытие строк): какой оператор/строка в блоке кода был покрыт?
Покрытие ветвей: какой путь выполнения был охвачен?
Покрытие условий: оценивается ли каждое булево подвыражение как истинное, так и ложное?
Не ведитесь на голые цифры. Не менее важно писать хорошие утверждения. Утверждения проверяют результат теста. Например, при записи файла вы захотите убедиться, что файл существует в нужном месте и содержит ожидаемые данные.
Помня об этой теории, давайте рассмотрим поддержку покрытия кода в Go.
Тулчеин Go имеет встроенные возможности для создания показателей покрытия кода. Мы уже использовали команду go test
раньше. Все, что вам нужно сделать, чтобы зафиксировать показатели покрытия кода, — указать параметр командной строки -cover
. Метрики будут отображаться прямо на терминале:go test ./... -cover
Вы также можете записать метрики в файл для дальнейшей обработки. Следующая команда записывает файлы в cover.txt
:
go test ./... -coverprofile=coverage.txt
После выполнения команды вы должны найти файл метрик. Обычно не нужно смотреть непосредственно на текстовый файл, но вы можете заглянуть в его структуру:
Итак, каким критериям покрытия кода следует Go? Критерии команды Go называются режимами покрытия и могут быть установлены с помощью параметра командной строки -covermode
. Вот общий обзор:
set
: Выполнялся ли каждый оператор?count
: сколько раз выполнялся каждый оператор?atomic
: работает как count, но считает точно в параллельных программах
Режим покрытия по умолчанию — это покрытие операторов, фактически установленное значение. Это критерии, которые мы применяли с предыдущей командой go test
. Не стесняйтесь исследовать другие режимы покрытия и определить, как меняются числа. На следующем шаге вы узнаете, как превратить метрики в визуальное представление.
Обычно удобнее видеть метрики в визуальном представлении. Тулчеин Go предлагает способ превратить метрики из текстового файла в отчет в формате HTML. Отчет в формате HTML отображает представление метрик с цветовой кодировкой в статическом файле HTML.
Давайте попробуем. Следующая команда создает файл HTML с именем index.html
.
go tool cover -html coverage.txt -o index.html
Существуют и другие варианты визуализации метрик покрытия кода. IDE может отображать метрики непосредственно в коде. Хорошими вариантами являются JetBrains GoLand и Microsoft Visual Studio Code с установленным плагином Go. Кроме того, вы также можете отправить метрики на стороннюю платформу, которая предоставляет расширенные функции визуализации. Coveralls и Codecov — лишь некоторые из них.
Напоследок хочу порекомендовать бесплатный вебинар от моих коллег из OTUS, на котором вы познакомитесь с профессией Тестировщика ПО, узнаете, где работают тестировщики и увидите какие задачи стоят перед тестировщиком в течение рабочего дня.
Комментарии (3)
sedyh
02.06.2023 11:46Здесь мы проверяем нулевое значение ошибки с помощью
assert.Nil
и равенство фактического с ожидаемым возвращаемым значением черезassert.Equal
Советую прочитать про разницу между assert и require, первый не остановит тест и вы можете получить npe в следующих проверках.
GradeBuilder
02.06.2023 11:46Не пробовали ли вы прикручивать AllureGo для красивого вывода метрик, ну и вообще как готовый тестовый фреймворк?
Plesser
поправьте меня если я не прав, но не только файл должен иметь суффикс _test но и сама функция должна иметь префикс с TestXXXX, причем с заглавной буквы (видимо это связано с правами доступа к ней).
ps Я только изучаю Golang, и вчера как раз слегка касался темы тестирования