I have a function which produces output to os.Stdout that I would like to unit test. How can I capture the output into a string which I can compare in my unit tests?
func f() {
// How to capture "hello\n"?
fmt.Fprintln(out, "hello")
}
CodePudding user response:
Write your function to take an optional io.Writer which defaults to stdout.
// f writes to stdout, or to an io.Writer if passed in.
func f(fd ...io.Writer) {
var out io.Writer
switch len(fd) {
case 0:
out = os.Stdout
case 1:
out = fd[0]
default:
panic("called with too many parms")
}
fmt.Fprintln(out, "hello")
}
For testing, pass a bytes.Buffer. When the function returns, the buffer will contain the output written by the function, ready for testing.
func main() {
// Writes to stdout
f()
// captures output in b
var b bytes.Buffer
f(&b)
fmt.Printf("<%s>\n", b.String())
}
An additional benefit of this approach is that the function can now easily write to a destination other than stdout if desired.
Playground: https://go.dev/play/p/3Kn_ht3ylE-
CodePudding user response:
One option is to use os.Pipe, and restore it with the os.Stdout
. One concern is that there's a potential race condition capturing/restoring os.Stdout
in a concurrent situation, however, maybe it could be Ok for testing purposes.
func main() {
storeStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// call f()
// for example just print hello
fmt.Println("hello")
w.Close()
out, _ := io.ReadAll(r)
// restore the stdout
os.Stdout = storeStdout
fmt.Printf("Got result: %s", out)
}