Пройти курс: Тестирование

В этом уроке мы поговорим о тестировании в Go. Итак, начнем с простого примера.

Мы создали пакет math, который содержит функцию Add, которая, как следует из названия, складывает два целых числа.

package math

func Add(a, b int) int {
    return a + b
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Она используется в нашем пакете main следующим образом.

package main

import (
    "example/math"
    "fmt"
)

func main() {
    result := math.Add(2, 2)
    fmt.Println(result)
}

Войти в полноэкранный режим Выход из полноэкранного режима

И если мы запустим это, то увидим результат.

$ go run main.go
4
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь мы хотим протестировать нашу функцию Add. Поэтому в Go мы объявляем файлы тестов с суффиксом _test в имени файла. Поэтому для нашего add.go мы создадим тест add_test.go. Структура нашего проекта должна выглядеть следующим образом.

.
├── go.mod
├── main.go
└── math
    ├── add.go
    └── add_test.go
Вход в полноэкранный режим Выход из полноэкранного режима

Мы начнем с использования пакета math_test и импорта пакета testing из стандартной библиотеки. Именно так! Тестирование встроено в Go, в отличие от многих других языков.

Но подождите… почему мы должны использовать math_test в качестве нашего пакета, разве мы не можем просто использовать тот же пакет math?

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

Теперь мы можем создать нашу функцию TestAdd. Она будет принимать аргумент типа testing.T, который предоставит нам полезные методы.

package math_test

import "testing"

func TestAdd(t *testing.T) {}
Вход в полноэкранный режим Выход из полноэкранного режима

Прежде чем добавлять логику тестирования, давайте попробуем запустить его. Но на этот раз мы не можем использовать команду go run, вместо этого мы воспользуемся командой go test.

$ go test ./math
ok      example/math 0.429s
Вход в полноэкранный режим Выйти из полноэкранного режима

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

$ go test ./...
?       example [no test files]
ok      example/math 0.348s
Вход в полноэкранный режим Выход из полноэкранного режима

И если Go не найдет ни одного теста в пакете, он сообщит нам об этом.

Отлично, давайте напишем тестовый код. Для этого мы проверим наш результат с ожидаемым значением, и если они не совпадут, мы можем использовать метод t.Fail, чтобы провалить тест.

package math_test

import "testing"

func TestAdd(t *testing.T) {
    got := math.Add(1, 1)
    expected := 2

    if got != expected {
        t.Fail()
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Отлично! Похоже, наш тест прошел.

$ go test math
ok      example/math    0.412s
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте также посмотрим, что произойдет, если мы провалим тест, для этого мы можем изменить наш ожидаемый результат.

package math_test

import "testing"

func TestAdd(t *testing.T) {
    got := math.Add(1, 1)
    expected := 3

    if got != expected {
        t.Fail()
    }
}
Войти в полноэкранный режим Выход из полноэкранного режима
$ go test ./math
ok      example/math    (cached)
Войти в полноэкранный режим Выход из полноэкранного режима

Если вы видите это, не волнуйтесь. Для оптимизации наши тесты кэшируются. Мы можем использовать команду go clean, чтобы очистить кэш, а затем повторно запустить тест.

$ go clean -testcache
$ go test ./math
--- FAIL: TestAdd (0.00s)
FAIL
FAIL    example/math    0.354s
FAIL
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, вот как будет выглядеть сбой теста.

Тесты, управляемые таблицами

Это подводит нас к тестам, управляемым таблицами. Но что это такое?

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

Не волнуйтесь, мы научимся этому на примере. Итак, мы начнем с определения нашей структуры addTestCase.

package math_test

import (
    "example/math"
    "testing"
)

type addTestCase struct {
    a, b, expected int
}

var testCases = []addTestCase{
    {1, 1, 3},
    {25, 25, 50},
    {2, 1, 3},
    {1, 10, 11},
}

func TestAdd(t *testing.T) {

    for _, tc := range testCases {
        got := math.Add(tc.a, tc.b)

        if got != tc.expected {
            t.Errorf("Expected %d but got %d", tc.expected, got)
        }
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, как мы объявили addTestCase с нижнего регистра. Правильно, мы не хотим экспортировать его, поскольку он не полезен за пределами нашей логики тестирования. Давайте запустим наш тест.

$ go run main.go
--- FAIL: TestAdd (0.00s)
    add_test.go:25: Expected 3 but got 2
FAIL
FAIL    example/math    0.334s
FAIL
Вход в полноэкранный режим Выход из полноэкранного режима

Похоже, что наши тесты сломались, давайте исправим их, обновив наши тестовые примеры.

var testCases = []addTestCase{
    {1, 1, 2},
    {25, 25, 50},
    {2, 1, 3},
    {1, 10, 11},
}
Вход в полноэкранный режим Выход из полноэкранного режима

Отлично, все работает!

$ go run main.go
ok      example/math    0.589s
Вход в полноэкранный режим Выход из полноэкранного режима

Покрытие кода

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

Для расчета и экспорта покрытия для нашего теста мы можем просто использовать аргумент -coverprofile с командой go test.

$ go test ./math -coverprofile=coverage.out
ok      example/math    0.385s  coverage: 100.0% of statements
Вход в полноэкранный режим Выход из полноэкранного режима

Похоже, у нас отличное покрытие. Давайте также проверим отчет с помощью команды go tool cover, которая дает нам подробный отчет.

$ go tool cover -html=coverage.out
Вход в полноэкранный режим Выход из полноэкранного режима

Как мы видим, это гораздо более читабельный формат. И что самое приятное, он встроен прямо в стандартный инструментарий.

Фазз-тестирование

Наконец, давайте рассмотрим фазз-тестирование, которое было введено в Go версии 1.18.

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

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

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

Попробуем рассмотреть пример:

func FuzzTestAdd(f *testing.F) {
    f.Fuzz(func(t *testing.T, a, b int) {
        math.Add(a , b)
    })
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Если мы запустим этот пример, то увидим, что он автоматически создаст тестовые случаи. Поскольку наша функция Add довольно проста, тесты пройдут.

$ go test -fuzz FuzzTestAdd example/math
fuzz: elapsed: 0s, gathering baseline coverage: 0/192 completed
fuzz: elapsed: 0s, gathering baseline coverage: 192/192 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 325017 (108336/sec), new interesting: 11 (total: 202)
fuzz: elapsed: 6s, execs: 680218 (118402/sec), new interesting: 12 (total: 203)
fuzz: elapsed: 9s, execs: 1039901 (119895/sec), new interesting: 19 (total: 210)
fuzz: elapsed: 12s, execs: 1386684 (115594/sec), new interesting: 21 (total: 212)
PASS
ok      foo 12.692s
Вход в полноэкранный режим Выход из полноэкранного режима

Но если мы обновим нашу функцию Add со случайным краевым случаем, например, программа запаникует, если b + 10 будет больше, чем a.

func Add(a, b int) int {
    if a > b + 10 {
        panic("B must be greater than A")
    }

    return a + b
}
Вход в полноэкранный режим Выход из полноэкранного режима

И если мы повторно запустим тест, этот краевой случай будет пойман с помощью fuzz-тестирования.

$ go test -fuzz FuzzTestAdd example/math
warning: starting with empty corpus
fuzz: elapsed: 0s, execs: 0 (0/sec), new interesting: 0 (total: 0)
fuzz: elapsed: 0s, execs: 1 (25/sec), new interesting: 0 (total: 0)
--- FAIL: FuzzTestAdd (0.04s)
    --- FAIL: FuzzTestAdd (0.00s)
        testing.go:1349: panic: B is greater than A
Войти в полноэкранный режим Выход из полноэкранного режима

Я считаю, что это очень хорошая функция Go 1.18. Вы можете узнать больше о фазз-тестировании в официальном блоге Go.


Эта статья является частью моего открытого курса по Go, доступного на Github.

karanpratapsingh / go-course

Освойте основы и расширенные возможности языка программирования Go

Курс по Go

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

Этот курс также доступен на моем сайте, а также на Educative.io

Оглавление

  • Начало работы

    • Что такое Go?
    • Зачем изучать Go?
    • Установка и настройка
  • Глава I

    • Hello World
    • Переменные и типы данных
    • Форматирование строк
    • Управление потоком данных
    • Функции
    • Модули
    • Пакеты
    • Рабочие пространства
    • Полезные команды
    • Сборка
  • Глава II

    • Указатели
    • Структуры
    • Методы
    • Массивы и фрагменты
    • Карты
  • Глава III

    • Интерфейсы
    • Ошибки
    • Паника и восстановление
    • Тестирование
    • Дженерики
  • Глава IV

    • Параллелизм
    • Гороутины
    • Каналы
    • Выбрать
    • Пакет синхронизации
    • Расширенные шаблоны параллелизма
    • Контекст
  • Приложение

    • Следующие шаги
    • Ссылки

Что такое Go?

Go (также известный как Golang) — это язык программирования, разработанный в Google в 2007 году и открытый в 2009 году.

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

Посмотреть на GitHub

Оцените статью
devanswers.ru
Добавить комментарий