Home > Back-end >  How do I capture os.Stdout output into a string for testing purposes?
How do I capture os.Stdout output into a string for testing purposes?

Time:11-04

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)
}

Playground

  • Related