web-dev-qa-db-fra.com

Comment tester les principales fonctions du package dans Golang?

Je veux tester quelques fonctions incluses dans mon package principal, mais mes tests ne semblent pas pouvoir accéder à ces fonctions.

Mon exemple de fichier main.go ressemble à ceci:

package main

import (
    "log"
)

func main() {
    log.Printf(foo())
}

func foo() string {
    return "Foo"
}

et mon fichier main_test.go ressemble à:

package main

import (
    "testing"
)

func Foo(t testing.T) {
    t.Error(foo())
}

quand je cours go test main_test.go Je reçois

# command-line-arguments
.\main_test.go:8: undefined: foo
FAIL    command-line-arguments [build failed]

Si je comprends bien, même si j'ai déplacé le fichier de test ailleurs et essayé d'importer à partir du fichier main.go, je ne pouvais pas l'importer, car il est package main.

Quelle est la bonne façon de structurer de tels tests? Dois-je simplement tout supprimer du package main en plus d'une simple fonction principale pour tout exécuter, puis tester les fonctions dans leur propre package, ou existe-t-il un moyen pour moi d'appeler ces fonctions à partir du fichier principal pendant le test?

28
ThePiachu

lorsque vous spécifiez des fichiers sur la ligne de commande, vous devez tous les spécifier

Voici ma course:

$ ls
main.go     main_test.go
$ go test *.go
ok      command-line-arguments  0.003s

note, dans ma version, j'ai couru avec main.go et main_test.go sur la ligne de commande

De plus, votre fichier _test n'est pas tout à fait correct, vous devez appeler votre fonction de test TestXXX et prendre un pointeur sur testing.

Voici la version modifiée:

package main

import (
    "testing"
)

func TestFoo(t *testing.T) {
    t.Error(foo())
}

et la sortie modifiée:

$ go test *.go
--- FAIL: TestFoo (0.00s)
    main_test.go:8: Foo
FAIL
FAIL    command-line-arguments  0.003s
19
David Budworth

Les tests unitaires ne vont que jusqu'à présent. À un moment donné, vous devez réellement exécuter le programme. Ensuite, vous testez qu'il fonctionne avec une entrée réelle, à partir de sources réelles, produisant une sortie réelle vers des destinations réelles. Pour de vrai.

Si vous voulez tester un élément, déplacez-le hors de main ().

9
Zan Lynx

C'est pas une réponse directe à la question de l'OP et je suis généralement d'accord avec les réponses et les commentaires précédents demandant que main soit principalement un appelant de fonctions packagées. Cela étant dit, voici une approche que je trouve utile pour tester les exécutables. Il utilise log.Fataln Et exec.Command.

  1. Écrivez main.go Avec une fonction différée qui appelle log.Fatalln () pour écrire un message à stderr avant de revenir.
  2. Dans main_test.go, Utilisez exec.Command(...) et cmd.CombinedOutput() pour exécuter votre programme avec des arguments choisis pour tester le résultat attendu.

Par exemple:

func main() {
    // Ensure we exit with an error code and log message
    // when needed after deferred cleanups have run.
    // Credit: https://medium.com/@matryer/golang-advent-calendar-day-three-fatally-exiting-a-command-line-tool-with-grace-874befeb64a4
    var err error
    defer func() {
        if err != nil {
            log.Fatalln(err)
        }
    }()

    // Initialize and do stuff

    // check for errors in the usual way
    err = somefunc()
    if err != nil {
        err = fmt.Errorf("somefunc failed : %v", err)
        return
    }

    // do more stuff ...

 }

Dans main_test.go, Un test pour, disons, de mauvais arguments qui devraient entraîner l'échec de somefunc pourrait ressembler à:

func TestBadArgs(t *testing.T) {
    var err error
    cmd := exec.Command(yourprogname, "some", "bad", "args")
    out, err := cmd.CombinedOutput()
    sout := string(out) // because out is []byte
    if err != nil && !strings.Contains(sout, "somefunc failed") {
        fmt.Println(sout) // so we can see the full output 
        t.Errorf("%v", err)
    }
}

Notez que err de CombinedOutput() est le code de sortie différent de zéro de log.Fatalln sous-le-capot appel à os.Exit(1). C'est pourquoi nous devons utiliser out pour extraire le message d'erreur de somefunc.

Le package exec fournit également cmd.Run Et cmd.Output. Celles-ci peuvent être plus appropriées que cmd.CombinedOutput Pour certains tests. Je trouve également utile d'avoir une fonction TestMain(m *testing.M) qui effectue la configuration et le nettoyage avant et après l'exécution des tests.

func TestMain(m *testing.M) {
    // call flag.Parse() here if TestMain uses flags
    os.Mkdir("test", 0777) // set up a temporary dir for generate files

    // Create whatever testfiles are needed in test/

    // Run all tests and clean up
    exitcode := m.Run()
    os.RemoveAll("test") // remove the directory and its contents.
    os.Exit(exitcode)
1
Mike Ellis