如何在Go测试输出中确定行号?

如何在Go测试输出中确定行号?

问题描述:

Let's consider this simple testing code.

(Note: assertSomething is super simple here, but normally I'd write a more specialised helper for the task at hand that would look at multiple things and could report more than one type of error.)

package hello

import "testing"

func TestFoo(t *testing.T) {
    assertSomething(t, 2+2 == 4) // line 6
    assertSomething(t, 2+3 == 6) // line 7
}

func assertSomething(t *testing.T, expected bool) {
    if !expected {
        t.Error("Something's not right") // line 12
    }
}

When I run go test, I get the following:

--- FAIL: TestFoo (0.00s)
    hello.go:12: Something's not right
FAIL
exit status 1
FAIL    kos/hello   0.008s

I have two questions:

1) The error points to line 12 - why? How does t.Error find out which line it was called from?

2) In the helper, I'd like to specify that t.Error should look stack level higher to determine the line number to print, so that I would get a message like this:

--- FAIL: TestFoo (0.00s)
    hello.go:7: Something's not right

Python allows me to do this, for instance, in warnings.warn("message", stacklevel=2) - how would I implement the equivalent here?

让我们考虑一下这个简单的测试代码。 p>

(注意: assertSomething code>在这里非常简单,但是通常我会为手头的任务编写一个更专业的助手,该助手会查看多种情况并报告多种错误。) em>

 包hello 
 
import“ testing” 
 
func TestFoo(t * testing.T){
 assertSomething(t,2 + 2 == 4)// 第6行
 assertSomething(t,2 + 3 == 6)//第7行
} 
 
func assertSomething(t * testing.T,预期的布尔值){
如果!expected {
 t.Error(  “某事不正确”)//第12行
} 
} 
  code>  pre> 
 
 

当我运行 go test code>时,我得到以下信息 : p>

  ---失败:TestFoo(0.00s)
 hello.go:12:某些地方不正确
FAIL 
exit状态1 
FAIL kos / hello 0.008s \  n  code>  pre> 
 
 

我有两个问题: p>

1)错误指向第12行-为什么? t.Error code>如何找出从哪一行调用的? p>

2)在帮助器中,我想指定该 t。 错误 code>应该看起来在堆栈级别更高,以确定要打印的行号,这样我会收到如下消息: p>

  --- FAIL:TestFoo(0.00  s)
 hello.go:7:有些不对
  code>  pre> 
 
 

例如,Python允许我在 warnings.warn(” message“,stacklevel = 2) code> - 我在这里实现等效功能吗? p> div>

Things have changed since go 1.9.

Helper() method has been added to testing.T and testing.B. It's intended to be invoked from testing helpers such as assertSomething to indicate the function is a helper and we're not interested in line numbers coming from it.

package main

import "testing"

func TestFoo(t *testing.T) {
    assertSomething(t, 2+2 == 4) // line 6
    assertSomething(t, 2+3 == 6) // line 7
}

func assertSomething(t *testing.T, expected bool) {
    if !expected {
        t.Helper()
        t.Error("Something's not right") // line 12
    }
}

The output contains correct line numbers:

=== RUN   TestFoo
--- FAIL: TestFoo (0.00s)
    main.go:7: Something's not right
FAIL

You can also try it on Go Playground.

You can do what you're asking, and you can find out how t.Error works by looking at the source code. The function decorate is what you're looking for I think.

But, in the case where you have significant amounts of checking code, and for some reason it's getting duplicated in your test, it's better to extract that as a function that returns an error than passing in a testing.T and making it an "assertion". Indeed, writing assertion functions is explicitly discouraged in the language FAQ.

package hello

import "testing"

func TestFoo(t *testing.T) {
    if err := checkSomething(2+2 == 4); err != nil {
        t.Errorf("2+2=4 failed: %s", err)
    }
    if err := checkSomething(2+3 == 6); err != nil {
        t.Errorf("2+3=6 failed: %s", err)
    }
}

func checkSomething(v bool) error {
    if !v {
        return errors.New("something's not right")
    }
    return nil
}

But here's what I think idiomatic testing code would look like. It's table-driven, and the cases include inputs and expected output, leading to really clear error messages when the tests fail.

package hello

import "testing"

func TestFoo(t *testing.T) {
    cases := []struct {
        a, b, want int
    }{
        {2, 2, 4},
        {2, 3, 6},
    }
    for _, c := range cases {
        if got := operation(c.a, c.b); got != c.want {
            t.Errorf("operation(%d, %d) = %d, want %d", c.a, c.b, got, c.want)
        }
    }
}

func operation(a, b int) int {
    return a + b
}