Run tests programmatically in Go

I have some tests that I'd like to run programmatically in Go. I'm trying to use testing.RunTests but it's raising a runtime error. I can't figure out what's wrong with the code either.

This is what it looks like:

package main

import (
    "testing"
)

func TestSomething(t *testing.T) {
    if false {
        t.Error("This is a mocked failed test")
    }
}

func main() {
    testing.RunTests(func(pat, str string) (bool, error) { return true, nil },
        []testing.InternalTest{
            {"Something", TestSomething}},
    )
}

Playground link: https://play.golang.org/p/BC5MG8WXYGD

The error I'm getting is:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4b5948]

First of all, running tests should be done via the go test command.

Certain types and functions exported in the testing package are for the testing framework, not for you. Quoting from testing.RunTests():

RunTests is an internal function but exported because it is cross-package; it is part of the implementation of the "go test" command.

It "had" to be exported because it predates "internal" packages.

There. You've been warned.

If you still want to do it, call testing.Main() instead of testing.RunTests().

For example:

func TestGood(t *testing.T) {
}

func TestBad(t *testing.T) {
    t.Error("This is a mocked failed test")
}

func main() {
    testing.Main(
        nil,
        []testing.InternalTest{
            {"Good", TestGood},
            {"Bad", TestBad},
        },
        nil, nil,
    )
}

Which will output (try it on the Go Playground):

--- FAIL: Bad (0.00s)
    prog.go:11: This is a mocked failed test
FAIL

If you want to capture the success of the testing, use the "newer" testing.MainStart() function.

First we need a helper type (which implements an unexported interface):

type testDeps struct{}

func (td testDeps) MatchString(pat, str string) (bool, error)   { return true, nil }
func (td testDeps) StartCPUProfile(w io.Writer) error           { return nil }
func (td testDeps) StopCPUProfile()                             {}
func (td testDeps) WriteProfileTo(string, io.Writer, int) error { return nil }
func (td testDeps) ImportPath() string                          { return "" }
func (td testDeps) StartTestLog(io.Writer)                      {}
func (td testDeps) StopTestLog() error                          { return nil }
func (td testDeps) SetPanicOnExit0(bool)                        {}

And now using it:

m := testing.MainStart(testDeps{},
    []testing.InternalTest{
        {"Good", TestGood},
        {"Bad", TestBad},
    },
    nil, nil,
)

result := m.Run()
fmt.Println(result)

Which outputs (try it on the Go Playground):

--- FAIL: Bad (0.00s)
    prog.go:13: This is a mocked failed test
FAIL
1

If all tests pass, result will be 0.


There is a way to fire a command

go test -v -json 

inside the code through exec.Command, initialise a reader that will read from stdout and then parse the output.

This answer was given by https://github.com/AlekSi Very grateful, thank you Aleksi!

https://github.com/FerretDB/dance/blob/main/internal/gotest/gotest.go