I just discovered that we can generate a JSON output of test results in Golang. I found this here.
Let's take a fresh simple example.
package jsontest func hello() string { return "Hello, World!" }
package jsontest import ( "testing" ) func TestHello(t *testing.T) { want := "Hello, World!" got := hello() t.Logf("got: %q", got) if got != want { t.Errorf("got: %q, want: %q", got, want) } }
Here, we have a function hello
that simply returns a string, the TestHello
function in the jsontest
package will check if the returned string is correctly returned or not.
So, we can test this with go test ./...
command, this will give out the output in a standard output/error in text format. However, if we add the -json
flag, we can get the output in JSON format.
go test ./... -json
{"Time":"2024-01-01T20:52:45.861974085+05:30","Action":"start","Package":"json-test"} {"Time":"2024-01-01T20:52:45.863133332+05:30","Action":"run","Package":"json-test","Test":"TestHello"} {"Time":"2024-01-01T20:52:45.863142397+05:30","Action":"output","Package":"json-test","Test":"TestHello","Output":"=== RUN TestHello\n"} {"Time":"2024-01-01T20:52:45.863148346+05:30","Action":"output","Package":"json-test","Test":"TestHello","Output":" main_test.go:10: got: \"Hello, World\"\n"} {"Time":"2024-01-01T20:52:45.863151351+05:30","Action":"output","Package":"json-test","Test":"TestHello","Output":" main_test.go:12: got: \"Hello, World\", want: \"Hello, World!\"\n"} {"Time":"2024-01-01T20:52:45.863157014+05:30","Action":"output","Package":"json-test","Test":"TestHello","Output":"--- FAIL: TestHello (0.00s)\n"} {"Time":"2024-01-01T20:52:45.863160418+05:30","Action":"fail","Package":"json-test","Test":"TestHello","Elapsed":0} {"Time":"2024-01-01T20:52:45.863411555+05:30","Action":"output","Package":"json-test","Output":"FAIL\n"} {"Time":"2024-01-01T20:52:45.863438344+05:30","Action":"output","Package":"json-test","Output":"FAIL\tjson-test\t0.001s\n"} {"Time":"2024-01-01T20:52:45.863443461+05:30","Action":"fail","Package":"json-test","Elapsed":0.001}
Pretty cool right?
This is really useful for programmatically taking the output and parsing it to get the metrics.
We can even combine with the coverage flag to get the coverage metrics as well.
go test ./... -json -cover
{"Time":"2024-01-01T21:13:30.771976961+05:30","Action":"start","Package":"jsontest"} {"Time":"2024-01-01T21:13:30.775118482+05:30","Action":"run","Package":"jsontest","Test":"TestHello"} {"Time":"2024-01-01T21:13:30.775172535+05:30","Action":"output","Package":"jsontest","Test":"TestHello","Output":"=== RUN TestHello\n"} {"Time":"2024-01-01T21:13:30.775201647+05:30","Action":"output","Package":"jsontest","Test":"TestHello","Output":" main_test.go:10: got: \"Hello, World!\"\n"} {"Time":"2024-01-01T21:13:30.775231759+05:30","Action":"output","Package":"jsontest","Test":"TestHello","Output":"--- PASS: TestHello (0.00s)\n"} {"Time":"2024-01-01T21:13:30.775253928+05:30","Action":"pass","Package":"jsontest","Test":"TestHello","Elapsed":0} {"Time":"2024-01-01T21:13:30.775269402+05:30","Action":"output","Package":"jsontest","Output":"PASS\n"} {"Time":"2024-01-01T21:13:30.776153185+05:30","Action":"output","Package":"jsontest","Output":"coverage: 100.0% of statements\n"} {"Time":"2024-01-01T21:13:30.776808599+05:30","Action":"output","Package":"jsontest","Output":"ok \tjsontest\t0.004s\tcoverage: 100.0% of statements\n" {"Time":"2024-01-01T21:13:30.777814589+05:30","Action":"pass","Package":"jsontest","Elapsed":0.006}
I am planning to use this in my workflow for integrating the output of the tests suite with specific tests.
For running the specific tests, you can use go test -run TestName
command, this will only run the provided test function.
// main.go package jsontest func hello() string { return "Hello, World!" } func add(x, y int) int { return x + y }
// main_test.go package jsontest import ( "testing" ) func TestHello(t *testing.T) { want := "Hello, World!" got := hello() t.Logf("got: %q", got) if got != want { t.Errorf("got: %q, want: %q", got, want) } } func TestAdd(t *testing.T) { want := 2 got := add(1, 1) t.Logf("got: %d", got) if got != want { t.Errorf("got: %d, want: %d", got, want) } }
So, we have two test in this go module, we can run a specific test using go test -run TestName
command as so:
go test -run TestAdd -json
{"Time":"2024-01-01T21:33:44.19397581+05:30","Action":"start","Package":"jsontest"} {"Time":"2024-01-01T21:33:44.198067398+05:30","Action":"run","Package":"jsontest","Test":"TestAdd"} {"Time":"2024-01-01T21:33:44.198150156+05:30","Action":"output","Package":"jsontest","Test":"TestAdd","Output":"=== RUN TestAdd\n"} {"Time":"2024-01-01T21:33:44.198197444+05:30","Action":"output","Package":"jsontest","Test":"TestAdd","Output":" main_test.go:19: got: 2\n"} {"Time":"2024-01-01T21:33:44.198217057+05:30","Action":"output","Package":"jsontest","Test":"TestAdd","Output":"--- PASS: TestAdd (0.00s)\n"} {"Time":"2024-01-01T21:33:44.198230965+05:30","Action":"pass","Package":"jsontest","Test":"TestAdd","Elapsed":0} {"Time":"2024-01-01T21:33:44.198241628+05:30","Action":"output","Package":"jsontest","Output":"PASS\n"} {"Time":"2024-01-01T21:33:44.19869148+05:30","Action":"output","Package":"jsontest","Output":"ok \tjsontest\t0.004s\n"} {"Time":"2024-01-01T21:33:44.198822637+05:30","Action":"pass","Package":"jsontest","Elapsed":0.005}
As we can see, there is only one test being executed and the output of the test is in JSON format.
These are really good flags and options to have as they make the output more portable. I will be planning to use this to improve my workflow in testing and developing open source projects and personal projects as well. I am really inspired by the Teej's video of executing anything in NeoVim.
<a class='prev' href='/vim-python-black-autoformat'>
<svg width="50px" height="50px" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 8.25L9.75 12L13.5 15.75" stroke="var(--prevnext-color-angle)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> </path>
</svg>
<div class='prevnext-text'>
<p class='prevnext-subtitle'>prev</p>
<p class='prevnext-title'>Autoformat Python file with Black after saving in Vim</p>
</div>
</a>
<a class='next' href='/dockerize-django-prj'>
<div class='prevnext-text'>
<p class='prevnext-subtitle'>next</p>
<p class='prevnext-title'>Dockerize a Django project</p>
</div>
<svg width="50px" height="50px" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 15.75L14.25 12L10.5 8.25" stroke="var(--prevnext-color-angle)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</a>