Say we have a function that logs its inputs and result:
package add
import (
"log"
)
func add(x, y int) int {
log.Printf("add %d %d", x, y)
res := x + y;
log.Printf("add %d %d = %d", x, y, res)
return res
}
And we have a failing unit test:
package add
import (
"testing"
)
func TestAdd(t *testing.T) {
inputs := []struct{
x int
y int
want int
}{{
x: 1,
y: 2,
want: 3,
}, {
x: 3,
y: 2,
want: 5,
}, {
x: 4,
y: 5,
want: 0, // fail
}}
for _, in := range inputs {
got := add(in.x, in.y)
if in.want != got {
t.Errorf(
"add %d %d got %d want %d",
in.x, in.y, got, in.want)
}
}
}
The output also contains the logs of all passing inputs:
$ go test
2020/09/05 09:01:25 add 1 2
2020/09/05 09:01:25 add 1 2 = 3
2020/09/05 09:01:25 add 3 2
2020/09/05 09:01:25 add 3 2 = 5
2020/09/05 09:01:25 add 4 5
2020/09/05 09:01:25 add 4 5 = 9
--- FAIL: TestAdd (0.00s)
add_test.go:28: add 4 5 got 9 want 0
FAIL
exit status 1
It would be better to only see the logs of the failing inputs. We can do that by logging to a buffer.
func add(logger *log.Logger, x, y int) int {
logger.Printf("add %d %d", x, y)
res := x + y
logger.Printf("add %d %d = %d", x, y, res)
return res
}
func TestAdd(t *testing.T) {
inputs := []struct {
x int
y int
want int
}{{
// ...
}}
for _, in := range inputs {
var buf bytes.Buffer
logger := log.New(&buf, "", log.LstdFlags)
got := add(logger, in.x, in.y)
if in.want != got {
t.Errorf(
"add %d %d log\n\n%s\ngot %d want %d",
in.x, in.y, buf.String(), got, in.want)
}
}
}
$ go test
--- FAIL: TestAdd (0.00s)
add_test.go:32: add 4 5 log
2020/09/05 09:22:48 add 4 5
2020/09/05 09:22:48 add 4 5 = 9
got 9 want 0
FAIL
exit status 1
We could also use constructor dependency injection like this post: Unit Test Using Commands Instead Of Mocks.