【问题标题】:How to check a log/output in go test?如何在 go test 中检查日志/输出?
【发布时间】:2017-10-22 12:42:38
【问题描述】:

我有这个功能可以在某些情况下记录错误:

func readByte(/*...*/){
    // ...
    if err != nil {
        fmt.Println("ERROR")
        log.Print("Couldn't read first byte")
        return
    }
    // ...
}

现在,在测试文件中,我想检查这个函数的输出错误:

    c.Assert(OUTPUT, check.Matches, "teste")

如何访问日志?我试图放置一个缓冲区,但它没有用。在不更改我的 readByte 函数代码的情况下捕获此日志的正确方法是什么?

【问题讨论】:

  • 将日志写入缓冲区有什么问题?
  • 缓冲区为空。输出返回“”。我检查日志是否正在写入并且没问题,但我的缓冲区总是空的。
  • 所以显示你是如何写入缓冲区的。没有理由不能写入缓冲区并稍后检查。
  • 什么是log.Print? Go 的 stdlib log 包?

标签: testing go


【解决方案1】:

例如,

readbyte_test.go:

package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "os"
    "testing"
)

func readByte( /*...*/ ) {
    // ...
    err := io.EOF // force an error
    if err != nil {
        fmt.Println("ERROR")
        log.Print("Couldn't read first byte")
        return
    }
    // ...
}

func TestReadByte(t *testing.T) {
    var buf bytes.Buffer
    log.SetOutput(&buf)
    defer func() {
        log.SetOutput(os.Stderr)
    }()
    readByte()
    t.Log(buf.String())
}

输出:

$ go test -v readbyte_test.go 
=== RUN   TestReadByte
ERROR
--- PASS: TestReadByte (0.00s)
    readbyte_test.go:30: 2017/05/22 16:41:00 Couldn't read first byte
PASS
ok      command-line-arguments  0.004s
$ 

【讨论】:

    【解决方案2】:

    并发测试答案

    如果您的测试同时运行(例如,在测试http ServerClient 时),您可能会遇到写入缓冲区和读取缓冲区之间的竞争。我们可以将输出重定向到os.Pipe,而不是缓冲区,并使用bufio.Scanner 进行阻塞,直到使用Scan() 方法写入输出。

    这里是创建os.Pipe 并设置stdlib log 包以使用管道的示例。注意我在这里使用testify/assert package

    func mockLogger(t *testing.T) (*bufio.Scanner, *os.File, *os.File) {
        reader, writer, err := os.Pipe()
        if err != nil {
            assert.Fail(t, "couldn't get os Pipe: %v", err)
        }
        log.SetOutput(writer)
    
        return bufio.NewScanner(reader), reader, writer
    }
    

    *os.File 对象被返回,因此可以使用延迟函数正确关闭它们。在这里,我只是打印到stdout,因为如果关闭时出现一些奇怪的错误,我个人不希望测试失败。但是,如果您愿意,这很可能是对 t.Errorf 或类似的另一个调用:

    func resetLogger(reader *os.File, writer *os.File) {
        err := reader.Close()
        if err != nil {
            fmt.Println("error closing reader was ", err)
        }
        if err = writer.Close(); err != nil {
            fmt.Println("error closing writer was ", err)
        }
        log.SetOutput(os.Stderr)
    }
    

    然后在你的测试中你会得到这个模式:

    scanner, reader, writer := mockLogger(t) // turn this off when debugging or developing as you will miss output!
    defer resetLogger(reader, writer)
    
    // other setup as needed, getting some value for thing below
    
    go concurrentAction() 
    
    scanner.Scan() // blocks until a new line is written to the pipe 
    
    got := scanner.Text() // the last line written to the scanner
    msg := fmt.Sprintf("your log message with thing %v you care about", thing) 
    assert.Contains(t, got, msg) 
    

    最后,concurrentAction() 函数正在调用 log 函数(或方法,如果使用 log.logger,包的行为实际上与上面的 log.SetOutput() 调用相同)如:

    // doing something, getting value for thing 
    log.Printf("your log message with the thing %v you care about", thing)
    

    【讨论】:

      猜你喜欢
      • 2014-09-15
      • 1970-01-01
      • 2019-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多