✅
Go:ファイルに依存するコードを対象としたテストでの No such file or directory を簡単に直す
go build
で得たバイナリを実行する時に、例えば bin/foobar
のように実行すると、current working directoryはもちろんbinの親ディレクトリになります。
一方で、 go test
はテストファイルのあるディレクトリをcurrent working directoryとして実行します。
そのため、テスト対象のコードにおいて、 os.ReadFile("foo/bar/data.csv")
や template.ParseFiles("templates/foo/bar.txt")
など、ローカルのファイルを相対パスで読み込んでいると、バイナリ実行の時は問題ないのに、テストでは No such file or directory
が起きます。
そんな時は、テストの先頭に下記を貼ると直せます。
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("failed to get current working directory: %v", err)
}
t.Cleanup(func() {
err := os.Chdir(cwd)
if err != nil {
t.Fatalf("failed to get current working directory: %v", err)
}
})
err := t, os.Chdir("../..")
if err != nil {
t.Fatalf("failed to get current working directory: %v", err)
}
Goなのでerrチェックが長くなるので、 t.Helper()
な関数に切り出して置くと良いかもしれません (deferを使っていないなので可能です)。下記のようにtestify/requireを使うのもおすすめです。
cwd, err := os.Getwd()
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, os.Chdir(cwd))
})
require.NoError(t, os.Chdir("../.."))
テストプロセスは同じディレクトリ (package?) の他のテストでも使いまわされるので、 Cleanup()
内でcurrent working directory元に戻しておきます。
Discussion
こんにちは。
元々どのようなテストを実行されたのか気になります。
実際のディレクトリ構成やテストコードを例示頂くことは可能ですか?
記事のタイトル・本文でその点が曖昧だったため少し修正しました🙏
テスト自体は、関数に引数を渡して返り値を確認するものです。
例えば下記のように、テスト対象がファイルに依存している場合を指していました。
ファイルの内容に当たる文字列をテストから引数として渡せば、ファイルへの依存をなくせますが、テンプレートファイルでそれをやるのは、テストのスコープを無理に狭める側面があると思っています。
ありがとうございます😆
これ以上は特に追いかける気はないのですが、以下参考情報です。
要旨は「テスト時の相対パス危ない」だと理解したのですが、何か特殊なことがないと記事中のエラーにはならないと思っており、どんなことをしているか具体的に知りたいなぁと思った次第でした。
テストの仕組みから上手に説明できたら良いのですが、そこまでの余裕がないので、結果系だけとなってしまい恐縮です。
こちらのGoのPNG readerのテストですが、「ファイルの内容をテストで読み込んで、テスト対象の引数に渡す」ものに見受けられます。この場合、ファイル読み込みはテストからしか実行されないので、ワーキングディレクトリは常にテストファイルのあるディレクトリです。
加筆修正した通り、わたしがエラーとなっていたのは、「テスト対象自体がファイルにアクセスする場合」で、その場合、テストから呼び出される場合と、バイナリとして起動される場合とで、ワーキングディレクトリが一致しないことがあります。
なるほど、理解できました。ありがとうございます👍