package foo
import (
"fmt"
"io"
"log"
"os"
"testing"
)
var
(
osExit = os.
Exit
logFatalf = log.Fatalf
)
type Tester
interface
{
Fatal(string, ...
interface
{})
Exit
(int)
}
type realTester struct{}
func (r realTester) Fatal(s string, v ...
interface
{}) {
log.Fatalf(s, v...)
}
func (r realTester)
Exit
(code int) {
os.
Exit
(code)
}
func Crasher() {
fmt.
Print
(
"Going down in flames!"
)
logFatalf(
"Exiting with code: %d"
, 1)
}
func TestCrasher(t *testing.T) {
tests := []struct {
name string
f func(t *testing.T, original Tester, tester *mockTester)
}{
{
"Test os.Exit()"
, func(t *testing.T, orig, test *mockTester) {
orig.
Exit
(1)
if
test.exitCode != 1 {
t.Errorf(
"expected exit code 1, got %d"
, test.exitCode)
}
}},
{
"Test log.Fatalf()"
, func(t *testing.T, orig, test *mockTester) {
orig.Fatalf(
"Exiting after a test failure"
)
if
test.format !=
"Exiting after a test failure"
{
t.Errorf(
"expected format \"Exiting after a test failure\", got %s"
, test.format)
}
}},
}
for
_, test := range tests {
t.Run(test.name, func(t *testing.T) {
var
orig Tester = realTester{}
var
mr mockTester
test.f(t, orig, &mr)
mr.Verify()
})
}
}
type mockTester struct {
format string
values []
interface
{}
exitCode int
exitCalls int
}
func (m *mockTester) Fatal(s string, v ...
interface
{}) {
m.format = s
m.values = v
m.
exit
()
}
func (m *mockTester)
Exit
(code int) {
m.exitCode = code
m.
exit
()
}
func (m *mockTester)
exit
() {
m.exitCalls++
}
func (m *mockTester) Verify() {
if
m.exitCalls != 1 {
panic(
"expected 1 call to Exit() or Fatal(), got %d"
, m.exitCalls)
}
}