Go: тестирование кода

Юнит-тестирование (unit testing, модульное тестирование) - это технология тестирования, цель которой уменьшить вероятность ошибок и побочных эффектов (когда при исправлении одного бага вносится другой баг). В чем идея юнит-тестирования? Вместо того, чтобы тестировать продукт целиком, что может оказаться весьма сложной и нетривиальной процедурой, нужно провести тестирование каждой самостоятельной единицы программного кода. Поскольку сейчас программный код в подавляющем большинстве пишется на объектно-ориентированных языках, то в качестве "кирпичика" кода выступает написанный программистом класс, а если переходить на ещё более глубокий уровень, то даже не класс, а отдельно взятый метод этого класса. Если каждый программист, проверит, что поведение его класса (модуля) соответствует задуманному, то и программа, состоящая из таких оттестированных классов, скорее всего, будет работать как задумано

Что значит — «программист проверит»? Это не означает, что он сделает это вручную. Вручную это делается по-старинке. Программист просматривает код, пытается представить как код будет работать… Это человеческий фактор. Человеку свойственно ошибаться. Проверит — означает, что программист напишет небольшую программку для тестирования поведения своего юнита (класса).

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

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

Давайте рассмотрим как на практике можно применить юнит-тесты для кода, написанного на модном на данный момент языке Go. Пусть у нас есть программа

package main

import "fmt"

func AVG( data ...float32 ) float32 {
    if len(data) == 0 {
        return 0
    }

    var sum float32 = 0

    for _, val := range data {
        sum += val
    }

    return sum / float32(len(data))
}

func main() {
    fmt.Println( AVG( 2.1, 3.7, 3.2 ) )
}

И так наша программа вызывает функцию AGV, которая вычисляет среднее знаечние переданных в нее аргументов. Давайте попробуем написать тест для проверки ее раотаспособности. Для этого в Go нужно знать следующее:

  • для тестрования используется пакет testing
  • тесты находятся в файлах *_test.go. Код в этих файлах не используется при сборке, только при тестах.
  • Команда go test проходится по всем файлам *_test.go и запускает тесты их них
  • Тесты определяются с помощью добавления Test к имени функции и принимают один аргумент типа *testing.T. В нашем случае, поскольку мы тестируем функцию AVG, тестирующая функция будет называться TestAVG

В результате получаем следующий Main_test.go файл:

package main

import "testing"

type testpair struct {
    values []float32
    average float32
}

var tests = []testpair{
    { []float32{1,2}, 1.5 },
    { []float32{1,1,1,1,1,1}, 1 },
    { []float32{-1,1}, 0 },
}

func TestAVG(t *testing.T) {
    for _, pair := range tests {
        v := AVG(pair.values...)
        if v != pair.average {
            t.Error(
                "For", pair.values,
                "expected", pair.average,
                "got", v,
            )
        }
    }
}

Запускаем тест:

~> go test
PASS
ok      _/home/lng/temp/test    0.003s

Тест прошел. Все отлично. Напрактике помимо выплнения тестов возникает необходимость оценить покрытие исходного кода тестами, для этого делаем вызов go test с ключем -cover

~> go test -cover
PASS
coverage: 71.4% of statements
ok      _/home/lng/temp/test    0.003s

Покрытие кода 71,4% процента

 

Тэги: go golang testing программирование тестрование


 


 
архив

подписка