Go:用于编写测试代码的类似C的宏

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

Go: C like macros for writing test code

问题

在编写测试代码时,我经常会这样做:

if !cond {
    t.Fatal("error message")
}

这有点繁琐。所以我想实现以下功能:

CHECK(cond, "error message")

所以我尝试了以下代码:

func CHECK(t *testing.T, cond bool, fmt string, a ...interface{}) {
    if !cond {
        t.Fatal(fmt, a)
    }
}

如果这是一个 C 宏,它应该能完美工作。但在 Go 语言中,失败的行号是错误的。

有没有解决这个问题的方法?

英文:

When writing test code, I do a lot of this

if (!cond) {
    t.Fatal("error message")
}

It's a bit tedious. So I'd like to achieve the following

CHECK(cond, "error message")

So I attempted this

func CHECK(t *testing.T, cond bool, fmt string, a ...interface{}) {
  if !cond {
  	t.Fatal(fmt, a)
  }
}

If it were a C macro it would've worked perfectly. But in Go, the line number where the failure is is wrong.

Is there a fix for this?

答案1

得分: 1

很遗憾,你不能这样做。

一个解决方法是自己获取行/函数,可以参考 https://stackoverflow.com/a/25954534/145587 中的跟踪函数。

英文:

Sadly you can't do that.

A workaround would be to get the line / function yourself, something like the trace function from https://stackoverflow.com/a/25954534/145587.

答案2

得分: 1

你可以使用runtime.Callers()runtime.Caller()来实现:第一个函数可以获取调用栈,而第二个函数可以提取任意堆栈帧(从调用栈列表中获取)的调试信息。

你的CHECK()函数总是在应该进行检查的位置下面一个函数调用的地方,所以你可以检查上面的堆栈帧。

更新:实际上只需要runtime.Caller()函数。以下是简化后的示例代码:

package main

import (
    "runtime"
    "testing"
)

func CHECK(t *testing.T, cond bool) {
    if !cond {
        _, fname, lineno, ok := runtime.Caller(1)
        if !ok {
            fname, lineno = "<UNKNOWN>", -1
        }
        t.Fatalf("FAIL: %s:%d", fname, lineno)
    }
}

func TestFoo(t *testing.T) {
    CHECK(t, 12 == 13)
}

将其保存为check_test.go并通过go test运行,将会输出:

$ go test
--- FAIL: TestFoo (0.00 seconds)
        check_test.go:14: FAIL: /home/kostix/devel/go/src/check/check_test.go:19
FAIL
exit status 1
FAIL    check   0.001s

其中第19行是在TestFoo()函数内部调用CHECK()的位置。

英文:

You could possibly make use of runtime.Callers()&plus;runtime.Caller(): the first one gives you the call stack while the second allows to extract the debug info about any arbitrary stack frame (obtained from that list).

Your CHECK() function is always one function call down the place the check should have happened at if it was a macro, so you can inspect the stack frame just above.


Update: the only functon which is really needed is runtime.Caller(). Here's your case, simplified:

package main

import (
    &quot;runtime&quot;
    &quot;testing&quot;
)

func CHECK(t *testing.T, cond bool) {
    if !cond {
        _, fname, lineno, ok := runtime.Caller(1)
        if !ok {
            fname, lineno = &quot;&lt;UNKNOWN&gt;&quot;, -1
        }
        t.Fatalf(&quot;FAIL: %s:%d&quot;, fname, lineno)
    }
}

func TestFoo(t *testing.T) {
    CHECK(t, 12 == 13)
}

When saved as check_test.go and run via go test, it produces:

$ go test
--- FAIL: TestFoo (0.00 seconds)
        check_test.go:14: FAIL: /home/kostix/devel/go/src/check/check_test.go:19
FAIL
exit status 1
FAIL    check   0.001s

where line 19 is the line a call to CHECK() is located inside TestFoo().

答案3

得分: 0

虽然使用CHECK()函数的上述答案是可行的,但我认为实际答案是代码的可读性。Go的许多设计都是为了在整个社区中增加可读性而做出的妥协。例如,可以看到gofmt。大多数人都会同意它的格式并不适用于每种情况。但是,通过所有人都同意的约定对于Go来说是一个巨大的优势。对于你的问题,答案也是一样的。Go是用来为你的同行编写代码的,而不是为了自己。所以不要想着“我更喜欢这个”。要想着“人们阅读我的代码会理解什么”。

你的原始代码应该像这样,不需要括号。

if !cond {
    t.Fatal("错误消息")
}

这是惯用的写法,每个Go程序员都会立刻认出它。这就是重点。

英文:

While the above answer to use CHECK() function will work, I think that the actual answer is code readibility. Much of Go has been designed as a compromise to increase readibility among the community as a whole. See gofmt for example. Most people will agree that it's format is not best for every case. But having a convention agreed to by all is a huge plus for Go. The same answer is to your question. Go is for writing code for your peers, not for yourself. So don't think "I prefer this." Think "what will people reading my code understand."

Your original code should be like this, without parenthesis.

if !cond {
    t.Fatal(&quot;error message&quot;)
}

This is idiomatic and every Go coder will recognize it instantly. That is the point.

huangapple
  • 本文由 发表于 2014年9月29日 17:22:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/26096496.html
匿名

发表评论

匿名网友

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

确定