Testy is the first Go library I’ve written to share with the community. The name is a play on testing and the emotion I had working with the standard testing package. I offer it up to help others that might feel the same way.
(Kind) feedback would be much appreciated. An excerpt from the README page follows.
##Testy – An extensible testing facade
If Go’s standard testing package annoys you, you might like Testy.
There is a lot to like about Go’s testing package.
There are also two extremely annoying things about it:
- You can’t refactor repetitive tests without reporting errors from the wrong place in the code.
- The testing package is locked down tight, preventing trivial solutions to the prior problem.
Testy implements a facade around the testing package and hijacks its logging features.
This means:
- You can report test errors at any level up the call stack.
- You can label all errors in a scope to disambiguate repetitive tests.
The downside is an extra level of log message nesting (which your editor’s quickfix window should ignore, anyway).
Unlike many other Go testing libraries, it doesn’t offer an extensive testing framework. Unlike some assertion libraries, it doesn’t use stack traces or race-prone print statements.
Testy offers a simple, extensible solution focused on reporting errors simply and in the right place. But it does give a few convenient helper functions for the most common tests you’re likely to write.
Examples
Using a custom helper function
package example
import (
"github.com/xdg/testy"
"testing"
)
func TestExample1(t *testing.T) {
is := testy.New(t)
defer func() { t.Logf(is.Done()) }() // Line 10
is.Error("First failure") // Line 11
checkTrue(is, 1+1 == 3) // Line 12
}
func checkTrue(is *testy.T, cond bool) {
if !cond {
is.Uplevel(1).Error("Expression was not true")
}
}
In the TestExample1
function, the is
variable wraps the test variable, t
. The defer
closure schedules test logging output to be delivered to t
via is.Done()
when the test function exits.
When run in Vim, with vim-go, the quickfix window looks like this:
_examples/example1_test.go|10| TestExample1: 2 tests failed
_examples/example1_test.go|11| First failure
_examples/example1_test.go|12| Expression was not true
Note that the checkTrue
error is reported from the call to checkTrue
at line 12, not from inside the checkTrue
function. The Uplevel
method in checkTrue
tells Testy to report the error one level up the call stack.
Using Testy helpers
The checkTrue
pattern is so common that testing true and false are built-in to Testy as True
and False
. There are also Equal
and Unequal
helpers that use reflect.DeepEqual
but provide diagnostic output on subsequent lines:
package example
import (
"github.com/xdg/testy"
"testing"
)
type pair struct {
x float32
y float32
}
func TestExample2(t *testing.T) {
is := testy.New(t)
defer func() { t.Logf(is.Done()) }()
is.True(1+1 == 3) // Line 17
is.False(2 == 2) // Line 18
is.Equal(1, 2) // Line 19
is.Equal(1.0, 1) // Line 20
is.Equal("foo\tbar", "foo\tbaz") // Line 21
is.Equal(true, false) // Line 22
is.Equal(&pair{1.0, 1.0}, &pair{1.1, 1.0}) // Line 23
is.Unequal(42, 42) // Line 24
}
The diagnostic output quotes strings and indicates types where necessary to disambiguate. For example:
_examples/example2_test.go|15| TestExample2: 8 tests failed
_examples/example2_test.go|17| Expression was not true
_examples/example2_test.go|18| Expression was not false
_examples/example2_test.go|19| Values were not equal:
|| Got: 1 (int)
|| Wanted: 2 (int)
_examples/example2_test.go|20| Values were not equal:
|| Got: 1 (float64)
|| Wanted: 1 (int)
_examples/example2_test.go|21| Values were not equal:
|| Got: "foo\tbar"
|| Wanted: "foo\tbaz"
_examples/example2_test.go|22| Values were not equal:
|| Got: true
|| Wanted: false
_examples/example2_test.go|23| Values were not equal:
|| Got: &{1 1} (*example.pair)
|| Wanted: &{1.1 1} (*example.pair)
_examples/example2_test.go|24| Values were not unequal:
|| Got: 42 (int)
Please see the full README file for more examples…