如何将 os.Stdout 的输出捕获到字符串中以进行测试目的?

huangapple go评论86阅读模式
英文:

How do I capture os.Stdout output into a string for testing purposes?

问题

我有一个函数,它会将输出发送到os.Stdout,我想对它进行单元测试。我该如何将输出捕获到一个字符串中,以便在我的单元测试中进行比较?

func f() {
    // 如何捕获 "hello\n"?
    fmt.Fprintln(out, "hello")
}

你可以使用bytes.Buffer来捕获输出并将其存储到一个字符串中。下面是一个示例:

import (
    "bytes"
    "fmt"
    "testing"
)

func TestF(t *testing.T) {
    var buf bytes.Buffer
    out = &buf

    f()

    expected := "hello\n"
    actual := buf.String()
    if actual != expected {
        t.Errorf("Expected: %q, but got: %q", expected, actual)
    }
}

在这个示例中,我们创建了一个bytes.Buffer对象来代替os.Stdout,然后将其赋值给out变量。在调用f()函数后,我们可以通过buf.String()方法获取捕获的输出,并将其与预期的输出进行比较。如果它们不相等,我们可以使用t.Errorf()方法来生成一个测试失败的错误消息。

英文:

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

答案1

得分: 1

一种选择是使用os.Pipe,并使用os.Stdout进行恢复。一个问题是在并发情况下捕获/恢复os.Stdout可能存在潜在的竞争条件,但是对于测试目的来说可能是可以接受的。

func main() {
	storeStdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	// 调用 f()
	// 例如只打印 hello
	fmt.Println("hello")

	w.Close()
	out, _ := io.ReadAll(r)
	// 恢复 stdout
	os.Stdout = storeStdout

	fmt.Printf("得到的结果:%s", out)
}

Playground

相关:请注意,log.SetOutput()可以用于捕获日志输出。

英文:

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: Note that log.SetOutput() can be used to capture logging output.

答案2

得分: 0

经典的测试用例方法

编写一个函数,该函数接受一个可选的io.Writer参数,默认为stdout,但可以传入一个io.Writer。这将使您能够将所有输出捕获到一个bytes.Buffer中,该缓冲区可以在测试用例中使用。

  // f函数将写入stdout,或者如果传入了io.Writer,则写入该io.Writer。
  func f(fd ...io.Writer) {
      var out io.Writer
      switch len(fd) {
      case 0:
          out = os.Stdout
      case 1:
          out = fd[0]
      default:
          panic("调用参数过多")
      }
      fmt.Fprintln(out, "hello")
  }

当您不传入参数调用该函数时,它将按照预期的方式将内容写入stdout。

      f()       // 写入stdout

为了进行测试,可以传入一个bytes.Buffer。当函数返回时,该缓冲区将包含函数写入的输出,可以用于测试。

      var b bytes.Buffer  // 在b中捕获输出
      f(&b)

      fmt.Printf("<%s>\n", b.String())

这种方法的另一个好处是,如果需要,函数现在可以轻松地写入除stdout之外的其他目标。

Playground: https://go.dev/play/p/3Kn_ht3ylE-

英文:

Classical Test Case Approach

Write your function to take an optional io.Writer which defaults to stdout, but lets you pass in an io.Writer. This will give you the ability to capture all output into a bytes.Buffer which can be used in the test cases.

  // 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(&quot;called with too many parms&quot;)
      }
      fmt.Fprintln(out, &quot;hello&quot;)
  }

When you call the function with no parameter, you get your usual expected operation of writing to stdout.

      f()       // Writes to stdout

For testing, pass a bytes.Buffer. When the function returns, the buffer will contain the output written by the function, ready for testing.

      var b bytes.Buffer  // capture output in b
      f(&amp;b)

      fmt.Printf(&quot;&lt;%s&gt;\n&quot;, 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-

huangapple
  • 本文由 发表于 2022年11月3日 15:13:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/74299209.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定