web-dev-qa-db-ja.com

golangのメインパッケージ機能をテストする方法は?

メインパッケージに含まれているいくつかの関数をテストしたいのですが、テストではそれらの関数にアクセスできないようです。

サンプルのmain.goファイルは次のようになります。

package main

import (
    "log"
)

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

func foo() string {
    return "Foo"
}

そして、私のmain_test.goファイルは次のようになります:

package main

import (
    "testing"
)

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

go test main_test.goを実行すると

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

理解できるように、テストファイルを他の場所に移動してmain.goファイルからインポートしようとしても、package mainであるためインポートできませんでした。

そのようなテストを構成する正しい方法は何ですか? mainパッケージからすべてを削除して、すべてを実行し、独自のパッケージ内の関数をテストする単純なメイン関数を追加する必要がありますか、またはテスト中にメインファイルからこれらの関数を呼び出す方法はありますか?

28
ThePiachu

コマンドラインでファイルを指定するときは、それらすべてを指定する必要があります

ここに私の実行があります:

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

私のバージョンでは、コマンドラインでmain.goとmain_test.goの両方で実行したことに注意してください

また、_testファイルは正しくありません。テスト関数をTestXXXと呼び、testing.Tへのポインターを取得する必要があります。

変更されたバージョンは次のとおりです。

package main

import (
    "testing"
)

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

変更された出力:

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

単体テストはこれまでのところです。ある時点で、実際にプログラムを実行する必要があります。次に、実際のソースからの実際の入力で動作することをテストし、実際の出力を実際の宛先に生成します。実際に。

単体テストを行いたい場合は、main()から移動してください。

9
Zan Lynx

これはnot OPの質問に対する直接的な回答であり、mainはほとんどがパッケージ化された関数の呼び出し元であるべきだという以前の回答とコメントに概ね同意しています。そうは言っても、実行可能ファイルのテストに役立つと思われるアプローチを次に示します。 _log.Fataln_および_exec.Command_を使用します。

  1. _main.go_を、log.Fatalln()を呼び出す遅延関数で記述して、戻る前にstderrにメッセージを書き込みます。
  2. _main_test.go_で、exec.Command(...)およびcmd.CombinedOutput()を使用して、予想される結果をテストするために選択した引数でプログラムを実行します。

例えば:

_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 ...

 }
_

_main_test.go_では、somefuncが失敗する原因となる不正な引数のテストは次のようになります。

_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)
    }
}
_

CombinedOutput()からのerrは、log.Fatallnのos.Exit(1)への内部呼び出しからのゼロ以外の終了コードであることに注意してください。そのため、outを使用してsomefuncからエラーメッセージを抽出する必要があります。

execパッケージは、_cmd.Run_および_cmd.Output_も提供します。これらは、一部のテストでは_cmd.CombinedOutput_よりも適切な場合があります。また、テストの実行前後にセットアップとクリーンアップを行うTestMain(m *testing.M)関数があると便利です。

_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