2015-11-05 5 views
6

У меня есть несколько различных реализаций интерфейса и множество факторов, которые я хочу проверить. Конечной целью является создание сетки результатов для разных реализаций в разных ситуациях.Можно ли динамически запускать контрольные тесты?

Я мог бы написать тест для каждой возможной комбинации, но получает утомительным:

func Benchmark_ImplA_N100_X300(b *testing.B){ 
    impl := newImplA(100,300) 
    runBenchmark(b,impl) 
} 

Чем больше комбинаций я добавляю, тем больше я должен копировать/вставить. Это быстро становится громоздким.

Я хотел бы сделать что-то вроде:

tests := []testing.InternalBenchmark{} 
for _, n := range []int{50,100,500,10000}{ 
    for _,x := range []int{300,200}{ 
     for name, gen := range implementations{ 
     impl = gen(n,x) 
     bm := testing.InternalBenchmark{Name: fmt.Sprint(name,x,n)} 
     bm.F = func(b *testing.B){ 
      runBench(b,impl) 
     } 
     tests = append(tests,bm) 
     } 
    } 
} 
testing.RunBenchmarks(anything, tests) 

, так что я могу обновить список (ы) комбинаций, и тесты будут волшебно работать во всех комбинациях. Я пытаюсь сделать что-то подобное в основном и в TestMain, и ничего не выйдет. Я не уверен, использую ли я его неправильно, или если пакет тестирования просто делает что-то смешное.

Мне действительно все равно, может ли инструмент go test справиться с этим, или если есть какой-то другой способ.

+0

простой скрипт оболочки с go: generate может работать также – kostya

ответ

5

Да, это возможно. В тестовом файле (xxx_test.go) создайте свою собственную функцию TestMain() и внутри нее после сборки динамических тестов (значения struct testing.InternalBenchmark) вызовите testing.Main(), который правильно разбирает флаги командной строки, создает и устанавливает testing.M и готовит и вызывает testing.RunBenchmarks(). Таким образом, ваши динамические тесты по-прежнему будут выполняться go test.

Примечания: testing.Main() никогда не будет возвращаться, поскольку он называет os.Exit(). Если вы хотите выполнить дальнейшие протоколирования и вычисления по результатам теста, вы также можете позвонить testing.MainStart().Run() (что и делает testing.Main()), и вы можете передать код выхода, который возвращается M.Run() в os.Exit().

Ниже приведен полный тестовый файл, который можно просто запустить с помощью go test -bench ..

Выход: результаты тестов динамически генерируемых тестов (различных реализаций с различными параметрами):

testing: warning: no tests to run 
PASS 
main.EngineA[impl=0, n=50, x=300]-4  100000    16716 ns/op 
main.EngineB[impl=1, n=50, x=300]-4  100000    24788 ns/op 
main.EngineA[impl=0, n=50, x=200]-4  100000    10764 ns/op 
main.EngineB[impl=1, n=50, x=200]-4  100000    16415 ns/op 
main.EngineA[impl=0, n=100, x=300]-4  50000    33426 ns/op 
main.EngineB[impl=1, n=100, x=300]-4  30000    48466 ns/op 
main.EngineA[impl=0, n=100, x=200]-4  50000    20452 ns/op 
main.EngineB[impl=1, n=100, x=200]-4  50000    33134 ns/op 
main.EngineA[impl=0, n=500, x=300]-4  10000   163087 ns/op 
main.EngineB[impl=1, n=500, x=300]-4  5000   238043 ns/op 
main.EngineA[impl=0, n=500, x=200]-4  10000   102662 ns/op 
main.EngineB[impl=1, n=500, x=200]-4  10000   163113 ns/op 
main.EngineA[impl=0, n=1000, x=300]-4  5000   319744 ns/op 
main.EngineB[impl=1, n=1000, x=300]-4  3000   512077 ns/op 
main.EngineA[impl=0, n=1000, x=200]-4  10000   201036 ns/op 
main.EngineB[impl=1, n=1000, x=200]-4  5000   325714 ns/op 
ok  _/xxx/src/play 27.307s 

И источник (тестовый файл, например dynbench_test.go):

package main 

import (
    "fmt" 
    "testing" 
) 

type Engine interface { 
    Calc() 
} 

type EngineA struct{ n, x int } 

func (e EngineA) Calc() { 
    for i := 0; i < e.n; i++ { 
     a, b := make([]byte, e.x), make([]byte, e.x) 
     copy(b, a) 
    } 
} 

type EngineB struct{ n, x int } 

func (e EngineB) Calc() { 
    for i := 0; i < e.n*2; i++ { 
     a, b := make([]byte, e.x/2), make([]byte, e.x/2) 
     copy(b, a) 
    } 
} 

func TestMain(m *testing.M) { 
    implementations := [](func(n, x int) Engine){ 
     func(n, x int) Engine { return EngineA{n, x} }, 
     func(n, x int) Engine { return EngineB{n, x} }, 
    } 

    benchmarks := []testing.InternalBenchmark{} 
    for _, n := range []int{50, 100, 500, 1000} { 
     for _, x := range []int{300, 200} { 
      for name, gen := range implementations { 
       impl := gen(n, x) 
       bm := testing.InternalBenchmark{ 
        Name: fmt.Sprintf("%T[impl=%d, n=%d, x=%d]", impl, name, n, x)} 
       bm.F = func(b *testing.B) { 
        for i := 0; i < b.N; i++ { 
         impl.Calc() 
        } 
       } 
       benchmarks = append(benchmarks, bm) 
      } 
     } 
    } 
    anything := func(pat, str string) (bool, error) { return true, nil } 

    testing.Main(anything, nil, benchmarks, nil) 
} 

Примечания № 2:

testing.Main(), testing.MainStart() и testing.InternalBenchmark может быть изменен (или удален) в следующем выпуске Go:

Внутренняя функция/внутренний тип, но экспортируется, поскольку это кросс-пакет; часть или вызвана выполнением команды «go test».

2

Я полагаю, что чтение документов поможет.Я не замечал

func Benchmark(f func(b *B)) BenchmarkResult

, который будет работать моя функция wothout потребность любого тестового жгута на всех.

Бенчмарк бенчмарков одной функции. Полезно для создания настраиваемых тестов , которые не используют команду «go test».

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

Смежные вопросы