Функция FuzzXxx

Функция FuzzXxx используется в пакете testing для создания фаззинг-тестов. Она принимает указатель на тип testing.F и выполняет тестирование с генерацией случайных данных. Фаззинг-тесты помогают обнаружить ошибки, которые сложно найти с помощью обычных тестов, такие как паника, выход за границы массива или некорректная обработка краевых случаев.

Синтаксис

gofunc FuzzXxx(f *testing.F) {
  // Добавление seed-корпусов
  f.Add(initialInput)

  // Определение фаззинг-функции
  f.Fuzz(func(t *testing.T, input ...interface{}) {
    // Тестируемый код с случайными входными данными
  })
}

Функция FuzzXxx должна принимать один параметр - указатель на testing.F. Внутри функции с помощью метода Add можно добавлять начальные значения (seed-корпусы), которые будут использоваться для генерации случайных данных. Метод Fuzz определяет фаззинг-функцию, которая принимает testing.T и список входных параметров.

Пример

Давайте создадим фаззинг-тест для функции, которая преобразует строку в целое число:

gopackage main

import (
  "strconv"
  "testing"
)

func FuzzParseInt(f *testing.F) {
  // Добавляем начальные значения для генерации
  f.Add("123")
  f.Add("0")
  f.Add("-456")

  f.Fuzz(func(t *testing.T, input string) {
    result, err := strconv.Atoi(input)
    if err != nil {
      // Для ошибок просто пропускаем
      t.Skip()
    }
    // Проверяем, что результат преобразуется обратно
    if strconv.Itoa(result) != input && input[0] != '+' {
      t.Errorf("Atoi и Itoa не обратны для %s", input)
    }
  })
}

Результат выполнения команды для запуска фаззинг-теста:

go"go test -fuzz=FuzzParseInt"

Пример

Рассмотрим фаззинг-тест для функции, которая работает с числами с плавающей точкой:

gopackage main

import (
  "math"
  "testing"
)

func FuzzSqrt(f *testing.F) {
  // Добавляем seed-корпусы
  f.Add(4.0)
  f.Add(0.0)
  f.Add(1.0)

  f.Fuzz(func(t *testing.T, x float64) {
    // Избегаем NaN и Inf
    if math.IsNaN(x) || math.IsInf(x, 0) {
      t.Skip()
    }
    // Вычисляем квадратный корень
    result := math.Sqrt(x)
    // Проверяем, что результат не NaN
    if math.IsNaN(result) {
      t.Errorf("Sqrt(%f) вернул NaN", x)
    }
    // Проверяем, что sqrt(x) >= 0 для x >= 0
    if x >= 0 && result < 0 {
      t.Errorf("Sqrt(%f) = %f, ожидается >= 0", x, result)
    }
  })
}

Результат выполнения кода:

go"fuzz: elapsed: 0s, execs: 1000 (1000/sec), new interesting: 2"

Пример

Фаззинг-тест для функции, которая работает со слайсами байт:

gopackage main

import (
  "bytes"
  "testing"
)

func FuzzReverse(f *testing.F) {
  // Добавляем начальные данные
  f.Add([]byte("hello"))
  f.Add([]byte("12345"))
  f.Add([]byte{})

  f.Fuzz(func(t *testing.T, data []byte) {
    // Проверяем, что реверс работает корректно
    original := make([]byte, len(data))
    copy(original, data)

    // Реверсируем слайс
    for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
      data[i], data[j] = data[j], data[i]
    }

    // Проверяем, что после двойного реверса получаем исходные данные
    for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
      data[i], data[j] = data[j], data[i]
    }

    if !bytes.Equal(data, original) {
      t.Errorf("Двойной реверс не вернул исходные данные")
    }
  })
}

Результат выполнения кода:

go"ok    example  0.234s"

Смотрите также

  • функцию testing.F,
    которая представляет собой фаззинг-контекст
  • метод testing.F.Add,
    который добавляет seed-корпусы для фаззинг-теста
  • метод testing.F.Fuzz,
    который определяет фаззинг-функцию
  • тип testing.T,
    который используется в фаззинг-функции для управления тестированием