How to fix line numbers in Go test results?

Consider this simple test code.

(Note: assertSomething is very simple here, but usually I write a more specialized assistant for a task that will look at several things and can report several types of errors.)

 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 not right") // line 12 } } 

When I run go test , I get the following:

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

I have two questions:

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

2) In the assistant, I would like to indicate that t.Error must look above the stack level in order to determine the line number for printing, so that I get the following message:

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

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

+6
source share
2 answers

Things have changed since 1.9.

A testing.T testing.B added to testing.T and testing.B

Helper() . It should be called from test helpers, such as assertSomething , to indicate that the function is a helper, and we are not interested in the 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) { t.Helper() if !expected { t.Error("Something not right") // line 12 } } 

The output contains the correct line numbers:

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

You can also try on the Go playground.

+1
source

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

But in the case when you have a significant amount of verification code, and for some reason it gets duplication in your test, it is better to extract it as a function that returns an error than pass it to test.T and make this β€œstatement”. Indeed, writing statement functions is clearly not recommended in the FAQ language.

 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 not right") } return nil } 

But here is what I think the idiomatic testing code will look like. It is managed by tables, and in cases include inputs and expected result, which leads to really clear error messages when 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(ca, cb); got != c.want { t.Errorf("operation(%d, %d) = %d, want %d", ca, cb, got, c.want) } } } func operation(a, b int) int { return a + b } 
+12
source

Source: https://habr.com/ru/post/983455/


All Articles