Reputation: 358
I am writing a package which compiles a C source file and writes the output to another file. I am writing tests for this package, and I need to create a temporary directory to write the output files. I am using TestMain
function to do this. For some reason, I always get the warning "no tests to run" when I just run the TestMain
test. I tried to debug the TestMain
function and I can see that the temporary directory is indeed created. When I create the testoutput
directory manually, all tests pass.
I am loading two C source files from testdata
directory, one of which is intentionally wrong.
gcc.go:
package gcc
import (
"os/exec"
)
func Compile(inPath, outPath string) error {
cmd := exec.Command("gcc", inPath, "-o", outPath)
return cmd.Run()
}
gcc_test.go:
package gcc
import (
"os"
"path/filepath"
"testing"
)
func TestOuputFileCreated(t *testing.T) {
var inFile = filepath.Join("testdata", "correct.c")
var outFile = filepath.Join("testoutput", "correct_out")
if err := Compile(inFile, outFile); err != nil {
t.Errorf("Expected err to be nil, got %s", err.Error())
}
if _, err := os.Stat(outFile); os.IsNotExist(err) {
t.Error("Expected output file to be created")
}
}
func TestOuputFileNotCreatedForIncorrectSource(t *testing.T) {
var inFile = filepath.Join("testdata", "wrong.c")
var outFile = filepath.Join("testoutput", "wrong_out")
if err := Compile(inFile, outFile); err == nil {
t.Errorf("Expected err to be nil, got %v", err)
}
if _, err := os.Stat(outFile); os.IsExist(err) {
t.Error("Expected output file to not be created")
}
}
func TestMain(m *testing.M) {
os.Mkdir("testoutput", 666)
code := m.Run()
os.RemoveAll("testoutput")
os.Exit(code)
}
go test
output:
sriram@sriram-Vostro-15:~/go/src/github.com/sriram-kailasam/relab/pkg/gcc$ go test
--- FAIL: TestOuputFileCreated (0.22s)
gcc_test.go:14: Expected err to be nil, got exit status 1
FAIL
FAIL github.com/sriram-kailasam/relab/pkg/gcc 0.257s
Running TestMain
:
Running tool: /usr/bin/go test -timeout 30s github.com/sriram-kailasam/relab/pkg/gcc -run ^(TestMain)$
ok github.com/sriram-kailasam/relab/pkg/gcc 0.001s [no tests to run]
Success: Tests passed.
Upvotes: 3
Views: 10842
Reputation: 4662
#1
Trying to run TestMain()
is like trying to run main()
. You don't do that, the OS does this for you.
TestMain was introduced in Go 1.4 to help with the setup/teardown of the test environment and is called instead of running the tests; quoting the release notes:
If the test code contains a function
func TestMain(m *testing.M)
that function will be called instead of running the tests directly. The M struct contains methods to access and run the tests.
#2
Use ioutil.TempDir()
to create temporary directories.
tmpDir, err := ioutil.TempDir("", "test_output")
if err != nil {
// handle err
}
It will take care of creating the directory. You should later use os.Remove(tmpDir)
to remove the temporary directory.
You can use it along with a slightly modified version of the suggestion from Tim Peoples, an example would be:
func TestCompile(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "testdata")
if err != nil {
t.Error(err)
}
defer os.Remove(tmpDir)
tests := []struct {
name, inFile, outFile string
err error
}{
{"OutputFileCreated", "correct.c", "correct_out", nil},
{"OutputFileNotCreatedForIncorrectSource", "wrong.c", "wrong_out", someErr},
}
for _, test := range tests {
var (
in = filepath.Join("testdata", test.inFile)
out = filepath.Join(tmpDir, test.outFile)
)
t.Run(test.name, func(t *testing.T) {
err = Compile(in, out)
if err != test.err {
t.Errorf("Compile(%q, %q) == %v; Wanted %v", in, out, err, test.err)
}
})
}
}
Upvotes: 5
Reputation: 354
Odds are, your problems are around the mode value you're passing to os.Mkdir(...)
. You're providing 666
decimal, which is 01232
octal (or if you prefer, a permissions string of d-w--wx-wT
) which I assume is not really what you're looking for.
Instead of 666
, you should specify 0666
-- the leading 0 indicates your value is in octal notation.
Also, your two tests are virtually identical; instead of using a TestMain(...)
to perform your setup, I'd suggest using *T.Run(...)
to execute your tests from a single, top-level Test*
func. Something like this:
gcc_test.go:
package gcc
import (
"testing"
"path/filepath"
"os"
)
const testoutput = "testoutput"
type testcase struct {
inFile string
outFile string
err error
}
func (tc *testcase) test(t *testing.T) {
var (
in = filepath.Join("testdata", tc.inFile)
out = filepath.Join(testoutput, tc.outFile)
)
if err := Compile(in, out); err != tc.err {
t.Errorf("Compile(%q, %q) == %v; Wanted %v", in, out, err, tc.err)
}
}
func TestCompile(t *testing.T) {
os.Mkdir(testoutput, 0666)
tests := map[string]*testcase{
"correct": &testcase{"correct.c", "correct_out", nil},
"wrong": &testcase{"wrong.c", "wrong_out", expectedError},
}
for name, tc := range tests {
t.Run(name, tc.test)
}
}
Upvotes: 2