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 rules are ordered alphabetically.
SHOULD comply with https://go.dev/blog
SHOULD comply with https://go.dev/doc
SHOULD comply with https://go.dev/doc/effective_go.html
SHOULD comply with https://go.dev/doc/faq
SHOULD comply with https://go.dev/wiki
SHOULD comply with https://go.dev/wiki/CodeReviewComments
SHOULD comply with https://go.dev/wiki/CodeReviewConcurrency
SHOULD comply with https://go.dev/wiki/Comments
SHOULD comply with https://go.dev/wiki/Errors
SHOULD comply with https://go.dev/wiki/TableDrivenTests
SHOULD comply with https://go.dev/wiki/TestComments
Good:
// Started earlier/* Started earlier */Bad:
//Started earlier/*Started earlier*/Good:
// Started earlier. Skip initialization.Bad:
// Started earlier. Skip initializationGood:
// Started earlier
skip()Bad:
// started earlier
skip()Some are tempted to use test frameworks on top of Go tests, 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 to use test frameworks 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 you 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 and teardown hooks. That's it. 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 and teardown with normal code and subtests.