Невозможно создать файл покрытия go

Я пробовал запустить sytemTest в этой статье: https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests

так что сначала следуйте советам, я создаю системный тестовый файл с именем main_test.go вот так:

func TestSystem(t *testing.T) {
    t.Logf("systemtest mod=%v", *SystemTest)
    if *SystemTest {
        t.Log("runing system test....")
        main()
    }
}

при выполнении этого модульного теста будет выполнена вся основная функция

затем я строю тестовый бинарник:

go test -c -covermode=count -coverpkg ./... -o main.test

и запустите тестовый файл birnary в моей тестовой среде

./main.test -systemTest  -test.coverprofile ./coverage.cov

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

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

если тест не вызывает main, профиль обложки может быть обработан обычным образом

См. Основную функцию

var mkrtExitWait sync.WaitGroup
var mkrtExitCode int
var mkrtRunning bool = false

func MKrtRun() int {
    mkrtExitWait.Add(1)
    mkrtRunning = true
    mkrtExitWait.Wait()
    return mkrtExitCode
}
func MKrtExit(code int) {
    if !mkrtRunning {
        os.Exit(code)
    } else {
        mkrtRunning = false
        mkrtExitCode = code
        mkrtExitWait.Done()
    }
}


func main() {
    // listen and serve code 
    ......

    if *SystemTest {  // a command flag 
        go func(){
            time.Sleep(time.Second * 10)
            MKrtExit(0)
        }()
    }
    MKrtRun()
}

Я пробовал несколько способов создать файл покрытия, как указано ниже, но это не сработало:

  1. отправить клиентский запрос, чтобы тестовый сервер выполнил os.Exit (0), когда программа запущена

  2. отправить клиентский запрос, чтобы сообщить тестовому серверу выполнить panic (), когда программа запущена

  3. убить процесс, чтобы принудительно выйти из программы

В чем проблема?

Как я могу создать файл покрытия?


person bruce    schedule 23.07.2018    source источник
comment
Не запускайте скомпилированный тестовый двоичный файл, но позвольте go test -cover выполнить работу.   -  person Volker    schedule 23.07.2018
comment
@Volker, моя среда компиляции и тестовая среда находятся на двух разных машинах, тестовый birnary компилируется на локальном компьютере, но запускается на другом удаленном сервере.   -  person bruce    schedule 23.07.2018
comment
Почему вам кажется, что профиль покрытия различается на разных машинах?   -  person Volker    schedule 23.07.2018
comment
просто потому, что тестовая программа даже не может работать на компилируемой машине, для запуска теста используется так много микросервисов, которые компилирующая машина не может предоставить   -  person bruce    schedule 23.07.2018


Ответы (4)


Я выяснил причину, по которой кавер-профиль нельзя генарировать ..

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

person bruce    schedule 24.07.2018
comment
У меня такая же проблема. Не могли бы вы подробнее рассказать о своем решении? - person iam thadiyan; 08.02.2019
comment
У меня такая же проблема. Я вызываю main () из моего файла _test.go внутри TestMain (). Я убедился, что это единственный тест в моем репо. Но все равно отчета нет. Не могли бы вы подробнее рассказать о своем решении? - person iam thadiyan; 08.02.2019
comment
Смотрите мой ответ, я развил эту подсказку - person ddrake12; 14.06.2019

Что решило это для меня (и упомянуто как часть ответа @darkwing), так это порядок флагов.

Когда я поместил -test.coverprofile=coverage.cov первым, он сработал, и был создан файл extension.cov.

Примечание: мне пришлось добавить знак = между именем флага и значением.

Также мне не нужно было запускать его в другом режиме.

person Elad Tabak    schedule 28.10.2019

ОП намекнул на ответ, но на самом деле его не дал. Ключ в том, что для написания профиля покрытия программе необходимо вернуть выполнение в TestMain и не может просто вызвать os.Exit(0)

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

go test -c -covermode=count -cover -coverpkg ./...

И затем он запускается следующим образом (кавычки были необходимы в PowerShell в Windows, но не в cmd):

testmain.test.exe "-test.coverprofile" coverage.cov systemTest

Я обнаружил, что по какой-то причине файл покрытия создается только в том случае, если мой собственный флаг systemTest идет после -test.coverprofile coverage.cov Вот простой пример:

main.go:

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    done := make(chan struct{})


    go func() {
        <-c
        done <- struct{}{}
    }()

    go func() {
        for {
            // this is where your real program runs
            fmt.Println("printing and sleeping")
            time.Sleep(1 * time.Second)
        }
    }()
    <-done
    return
}

main_test.go:

func TestMain(t *testing.T) {
    run := false

    for _, arg := range os.Args {
        switch {
        case arg == "systemTest":
            run = true
        }
    }

    if run {
        main()
        fmt.Println("returned from main")
    }
}

Вдохновение: https://www.cyphar.com/blog/post/20170412-golang-integration-coverage и https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests

person ddrake12    schedule 14.06.2019

Системное тестирование обычно используется, когда вы запускаете какие-то внешние тесты для вашего тестирования.

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

Это можно сделать, добавив следующие

        // Handle graceful shutdown of http server via OS Interrupt
        quit := make(chan os.Signal)
        signal.Notify(quit, os.Interrupt)

        // Block a goroutine on Interrupt channel and close
        // http server to exit gracefully
        go func() {
            <-quit
            log.Info("receive interrupt signal")
            MKrtEixt(0)
        }() 

Итак, ваши шаги тестирования будут

  1. Вызов тестируемой системы
  2. Запустить внешние тесты
  3. Выдать SIGINT тестируемой системе после завершения тестов
  4. Соберите файлы покрытия
person rajeshnair    schedule 23.07.2018
comment
пробовал это раньше, но не работает ,, и получился интересный результат: ошибка SIGINT не может остановить программу. Я отслеживал каждую выполненную строку, после MKrtEixt (0) - ›mkrtExitWait.Done () -› mkrtExitWait.Wait () - ›main.go return, но программа все еще работает, я думаю, это связано с тестовым birnary, если я запускаю main .go, SIGINT может остановить программу - person bruce; 23.07.2018
comment
os.Exit (0) или panic () могут остановить тестовую программу, но MKrtEixt (0) не может - person bruce; 23.07.2018
comment
Значит, у вашей MKrtEixt func есть ошибка! Я использовал этот метод, за исключением того, что мы должны останавливать http-сервер вместо вызова os.Exit () - person rajeshnair; 23.07.2018
comment
Я, вероятно, знаю причину, по которой main () return не может остановить тестовую программу, когда тестовый двоичный файл был построен, может быть добавлена ​​еще одна функция main () для выполнения функции TestSystem () , TestSystem (), затем выполнить функцию main () Я определил, что означает, что функция main (), которую мы видим, не является реальной функцией main (), которая выполняется в основной сопрограмме, поэтому, когда она возвращается, мастер-сопрограмма не имеет никакого эффекта, вся программа все еще выполняется, только путем выполнения os.Exit ( 0), можем ли мы остановить программу - person bruce; 24.07.2018
comment
В таком случае, как вы думаете, данный ответ правильный? Я предлагаю отредактировать ответ / вопрос, чтобы добавить свое предостережение. Это может быть кто-нибудь другой :-) - person rajeshnair; 24.07.2018