如何测试中,含有log.Fatal去功能()

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何测试中,含有log.Fatal去功能()相关的知识,希望对你有一定的参考价值。

我说,我有以下代码打印一些日志消息。我怎么会去测试正确的消息已经被记录?作为log.Fatal调用os.Exit(1)测试失败。

package main

import (
    "log"
)

func hello() 
    log.Print("Hello!")


func goodbye() 
    log.Fatal("Goodbye!")


func init() 
    log.SetFlags(0)


func main() 
    hello()
    goodbye()

这里是假设的测试:

package main

import (
    "bytes"
    "log"
    "testing"
)


func TestHello(t *testing.T) 
    var buf bytes.Buffer
    log.SetOutput(&buf)

    hello()

    wantMsg := "Hello!\n"
    msg := buf.String()
    if msg != wantMsg 
        t.Errorf("%#v, wanted %#v", msg, wantMsg)
    


func TestGoodby(t *testing.T) 
    var buf bytes.Buffer
    log.SetOutput(&buf)

    goodbye()

    wantMsg := "Goodbye!\n"
    msg := buf.String()
    if msg != wantMsg 
        t.Errorf("%#v, wanted %#v", msg, wantMsg)
    

答案

这类似于“How to test os.Exit() scenarios in Go”:你需要实现自己的记录,在默认情况下重定向log.xxx(),但给你的机会,在测试时,用自己取代像log.Fatalf()的功能(这不叫os.Exit(1)

我也做了同样的测试中os.Exit() exit/exit.go电话:

exiter = New(func(int) )
exiter.Exit(3)
So(exiter.Status(), ShouldEqual, 3)

(在这里,我的“退出”功能是一个空的这什么都不做)

另一答案

我曾尝试使用下面的代码来测试我的功能。在xxx.go:

var logFatalf = log.Fatalf

if err != nil 
    logFatalf("failed to init launcher, err:%v", err)

而在xxx_test.go:

// TestFatal is used to do tests which are supposed to be fatal
func TestFatal(t *testing.T) 
    origLogFatalf := logFatalf

    // After this test, replace the original fatal function
    defer func()  logFatalf = origLogFatalf  ()

    errors := []string
    logFatalf = func(format string, args ...interface) 
        if len(args) > 0 
            errors = append(errors, fmt.Sprintf(format, args))
         else 
            errors = append(errors, format)
        
    
    if len(errors) != 1 
        t.Errorf("excepted one error, actual %v", len(errors))
    

另一答案

虽然它可以测试包含log.Fatal代码,所以不推荐。特别是,你不能在由上-covergo test标志支持的方式测试代码。

相反,它建议你改变你的代码,改为返回调用log.Fatal的错误。在顺序功能,可以添加额外的返回值,并在一个够程可以类型chan error的信道(或包含错误类型的领域中的一些结构类型)上传递一个错误。

一旦做出更改您的代码会更容易阅读,更容易测试,这将是更便携(现在除了可以用它在服务器程序的命令行工具)。

如果你有log.Println调用我还建议通过自定义记录器作为接收器的领域。这样,你就可以登录到自定义记录器,你可以设置为标准错误或标准输出为一个服务器,并测试一个空操作记录器(这样你就不会得到你的测试一堆不必要的输出)。该log包支持自定义记录程序,所以没有必要写你自己的或导入第三方软件包这一点。

另一答案

如果您使用logrus,还有现在是时候从this commit推出V1.3.0定义你的退出功能的选项。所以,你的测试可能看起来是这样的:

func Test_X(t *testing.T) 
    cases := []struct
        param string
        expectFatal bool
    
        
            param: "valid",
            expectFatal: false,
        ,
        
            param: "invalid",
            expectFatal: true,
        ,
    

    defer func()  log.StandardLogger().ExitFunc = nil ()
    var fatal bool
    log.StandardLogger().ExitFunc = func(int) fatal = true 

    for _, c := range cases 
        fatal = false
        X(c.param)
        assert.Equal(t, c.expectFatal, fatal)
    

另一答案

我(与bouk/monkey一起在这里)使用超级方便stretchr/testify包。

func TestGoodby(t *testing.T) 
  wantMsg := "Goodbye!"

  fakeLogFatal := func(msg ...interface) 
    assert.Equal(t, wantMsg, msg[0])
    panic("log.Fatal called")
  
  patch := monkey.Patch(log.Fatal, fakeLogFatal)
  defer patch.Unpatch()
  assert.PanicsWithValue(t, "log.Fatal called", goodbye, "log.Fatal was not called")

我建议阅读caveats to using bouk/monkey才去这条路线。

另一答案

那里曾经是这里的答案我提及的,看起来像它删除了。这是我见过的,你可能已经通过测试,而不需要修改的依赖或以其他方式接触代码应该致命的唯一的一个。

我同意其他的答案,这通常是不恰当的测试。通常你应该重写被测代码返回一个错误,测试观察非零误差后返回的错误预期,并且致命处于较高的水平范围。

要测试的正确的消息已经被记录下来,你会检查内部流程的cmd.Stdout的OP的问题。

https://play.golang.org/p/J8aiO9_NoYS

func TestFooFatals(t *testing.T) 
    fmt.Println("TestFooFatals")
    outer := os.Getenv("FATAL_TESTING") == ""
    if outer 
        fmt.Println("Outer process: Spawning inner `go test` process, looking for failure from fatal")
        cmd := exec.Command(os.Args[0], "-test.run=TestFooFatals")
        cmd.Env = append(os.Environ(), "FATAL_TESTING=1")
        // cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
        err := cmd.Run()
        fmt.Printf("Outer process: Inner process returned %v\n", err)
        if e, ok := err.(*exec.ExitError); ok && !e.Success() 
            // fmt.Println("Success: inner process returned 1, passing test")
            return
        
        t.Fatalf("Failure: inner function returned %v, want exit status 1", err)
     else 
        // We're in the spawned process.
        // Do something that should fatal so this test fails.
        foo()
    


// should fatal every time
func foo() 
    log.Printf("oh my goodness, i see %q\n", os.Getenv("FATAL_TESTING"))
    // log.Fatal("oh my gosh")

另一答案

你不能和你不应该。这个“你必须‘测试’每一条线路” -attitude很奇怪,尤其是对终端条件,这就是log.Fatal是。 (或者只是从外部对其进行测试。)

以上是关于如何测试中,含有log.Fatal去功能()的主要内容,如果未能解决你的问题,请参考以下文章

如何做好功能测试

Go 包是不是应该使用 log.Fatal 以及何时使用?

如何做好功能测试?你确定不想知道吗?

功能测试如何做?测试大佬列出了这些关键信息!

小马8年经验之谈:难道就只能是点点点了?功能测试未来是在哪里?

测试“用户登录”功能