The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
The sections and conventions are ordered alphabetically.
These conventions closely follow Go idioms, Go best practicies, and the Go Way. They are the consensus of the Go community on how best to write Go. Most who write Go already do these things. Aside from the obvious benefits, it is important to write Go like the Go community so new engineers can more easily understand it.
SHOULD comply with https://golang.org/blog
SHOULD comply with https://golang.org/doc/effective_go.html
SHOULD comply with https://golang.org/wiki/CodeReviewComments
Good:
// Started earlier
/* Started earlier */
Bad:
//Started earlier
/*Started earlier*/
Good:
// Started earlier. Skip initialization.
Bad:
// Started earlier. Skip initialization
Good:
// Started earlier
if stopped() {
Bad:
// started earlier
if stopped() {
Some are tempted to use test suites on top of the standard Go test framework, like testify/suite
.
Some reasons to use plain Go tests:
- It's simpler
- It's fully capable
- It's widely understood
- It's built-in
- It's supported by the Go team, stable, idiomatic, and high-quality
- It's not a DSL
Some reasons not to use testify/suite
:
- It encourages shared state among test methods, which is difficult to reason about, extend, and parallelize. Most tests should be independent and able to run in parallel. The motivation for using suites goes away as soon as you isolate every test.
- You have to have a driver test in the Go test framework just to get it to work:
func TestFooTestSuite(t *testing.T) {
suite.Run(t, new(FooTestSuite))
}
- You have to write boilerplate just to do simple testing:
type FooTestSuite struct {
suite.Suite
}
func (suite *FooTestSuite) TestFoo() {
...
}
func TestFooTestSuite(t *testing.T) {
suite.Run(t, new(FooTestSuite))
}
Compare that to:
func TestFoo(t *testing.T) {
...
}
- It's a third-party, external dependency that we have to learn and maintain
If you look at suite.Suite
, it doesn't actually do all that much:
func (suite *Suite) Assert() *assert.Assertions
func (suite *Suite) Require() *require.Assertions
func (suite *Suite) Run(name string, subtest func()) bool
func (suite *Suite) SetT(t *testing.T)
func (suite *Suite) T() *testing.T
It just packages together shared assert.Assertions
, require.Assertions
, and testing.T
values, and provides a helper method for calling testing.T.Run
. suite.Run
provides support for setup/teardown hooks. That's it. Go tests already have a testing.T
, they can already use assert.Assertions
and require.Assertions
, and—as illustrated in the testing
package doc1, 2—it's trivial enough to do setup/teardown with normal Go code and subtests.