Sriram Kailasam
Sriram Kailasam

Reputation: 358

TestMain - no tests to run

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

Answers (2)

ifnotak
ifnotak

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

Tim Peoples
Tim Peoples

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

Related Questions