Как я могу выполнить тестовую настройку с помощью тестового пакета в Go

Как я могу выполнить полную обработку тестовых настроек, которая устанавливает основу для всех тестов при использовании пакета тестирования?

В качестве примера в Nunit есть атрибут [SetUp].

[TestFixture]
public class SuccessTests
{
  [SetUp] public void Init()
  { /* Load test data */ }
}

Ответ 1

Начиная с Go 1.4, вы можете реализовать настройку/демонтаж (не нужно копировать свои функции перед/после каждого теста). Документация изложена здесь в главном разделе:

TestMain работает в основной программе и может выполнять любые настройки и разборки, необходимые для вызова m.Run. Затем следует вызвать os.Exit с результатом m.Run

Мне потребовалось некоторое время, чтобы понять, что это означает, что если тест содержит функцию func TestMain(m *testing.M) то эта функция будет вызываться вместо запуска теста. И в этой функции я могу определить, как будут выполняться тесты. Например, я могу реализовать глобальную настройку и демонтаж:

func TestMain(m *testing.M) {
    setup()
    code := m.Run() 
    shutdown()
    os.Exit(code)
}

Несколько других примеров можно найти здесь.

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

Ответ 2

Это может быть достигнуто путем помещения функции init() в файл _test.go. Это будет выполняться до функции init().

// package_test.go
package main

func init() {
     /* load test data */
}

_Test.init() будет вызываться перед функцией init() пакета.

Ответ 3

Учитывая простую функцию модульного теста:

package math

func Sum(a, b int) int {
    return a + b
}

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

package math

import "testing"

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func setupSubTest(t *testing.T) func(t *testing.T) {
    t.Log("setup sub test")
    return func(t *testing.T) {
        t.Log("teardown sub test")
    }
}

func TestAddition(t *testing.T) {
    cases := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"add", 2, 2, 4},
        {"minus", 0, -2, -2},
        {"zero", 0, 0, 0},
    }

    teardownTestCase := setupTestCase(t)
    defer teardownTestCase(t)

    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            teardownSubTest := setupSubTest(t)
            defer teardownSubTest(t)

            result := Sum(tc.a, tc.b)
            if result != tc.expected {
                t.Fatalf("expected sum %v, but got %v", tc.expected, result)
            }
        })
    }
}

Инструмент тестирования Go будет сообщать о операторах ведения журнала в консоли консоли:

% go test -v
=== RUN   TestAddition
=== RUN   TestAddition/add
=== RUN   TestAddition/minus
=== RUN   TestAddition/zero
--- PASS: TestAddition (0.00s)
    math_test.go:6: setup test case
    --- PASS: TestAddition/add (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/minus (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/zero (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    math_test.go:8: teardown test case
PASS
ok      github.com/kare/go-unit-test-setup-teardown 0.010s
% 

Вы можете передать некоторые дополнительные параметры для настройки/удаления с помощью этого подхода.

Ответ 4

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

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

Если у вас все еще есть общий код установки между тестовыми функциями, вы можете извлечь общий код установки в функцию и использовать sync.Once если важно, чтобы он выполнялся ровно один раз (или, как предлагает другой ответ, используйте init(), но это имеет недостаток, что установка будет выполнена, даже если тестовые примеры не будут выполняться (возможно, потому, что вы ограничили тестовые примеры, используя go test -run <regexp>.)

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

Ответ 5

Рамка тестирования Go не имеет ничего эквивалентного атрибуту NUnit SetUp (обозначение функции, которая должна быть вызвана до каждого теста в пакете). Однако есть несколько вариантов:

  1. Просто вызовите функцию SetUp из каждого теста, где это необходимо.

  2. Используйте расширение для платформы тестирования Go, которая реализует парадигмы и концепции xUnit. На ум приходят три сильных варианта:

Каждая из этих библиотек рекомендует вам организовывать ваши тесты в наборах/светильниках, аналогичных другим фреймворкам xUnit, и будет вызывать методы настройки для типа suite/fixture перед каждым из методов Test*.