### Define and Run a Test Suite with Testify Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md This example demonstrates how to define a test suite using testify/suite, including setup methods and running the suite with `suite.Run`. ```go import ( t"testing" "github.com/stretchr/testify/suite" ) type ExampleTestSuite struct { suite.Suite VariableThatShouldStartAtFive int } func (suite *ExampleTestSuite) SetupTest() { suite.VariableThatShouldStartAtFive = 5 } func (suite *ExampleTestSuite) TestExample() { ssuite.Equal(suite.VariableThatShouldStartAtFive, 5) } func TestExampleTestSuite(t *testing.T) { ssuite.Run(t, new(ExampleTestSuite)) } ``` -------------------------------- ### Install Objx Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/objx/README.md Install the Objx package using the `go get` command. ```go go get github.com/stretchr/objx ``` -------------------------------- ### Install Testify Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Use `go get` to install the Testify testing framework. This makes packages like assert, require, mock, and suite available. ```bash go get github.com/stretchr/testify ``` -------------------------------- ### Install and Run TestifyLint Source: https://github.com/antonboom/testifylint/blob/master/README.md Install the linter globally using go install and run it against your Go modules. Use the -h flag for help. ```bash $ go install github.com/Antonboom/testifylint@latest $ testifylint -h $ testifylint ./... ``` -------------------------------- ### Ginkgo Spec Example Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/README.md Illustrates a typical Ginkgo spec structure with BeforeEach, When, Context, It, and SpecTimeout. It demonstrates how to define setup, conditional logic, and test cases with assertions using Gomega. ```go import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ... ) var _ = Describe("Checking books out of the library", Label("library"), func() { var library *libraries.Library var book *books.Book var valjean *users.User BeforeEach(func() { library = libraries.NewClient() book = &books.Book{ Title: "Les Miserables", Author: "Victor Hugo", } valjean = users.NewUser("Jean Valjean") }) When("the library has the book in question", func() { BeforeEach(func(ctx SpecContext) { Expect(library.Store(ctx, book)).To(Succeed()) }) Context("and the book is available", func() { It("lends it to the reader", func(ctx SpecContext) { Expect(valjean.Checkout(ctx, library, "Les Miserables")).To(Succeed()) Expect(valjean.Books()).To(ContainElement(book)) Expect(library.UserWithBook(ctx, book)).To(Equal(valjean)) }, SpecTimeout(time.Second * 5)) }) Context("but the book has already been checked out", func() { var javert *users.User BeforeEach(func(ctx SpecContext) { javert = users.NewUser("Javert") Expect(javert.Checkout(ctx, library, "Les Miserables")).To(Succeed()) }) It("tells the user", func(ctx SpecContext) { err := valjean.Checkout(ctx, library, "Les Miserables") Expect(err).To(MatchError("Les Miserables is currently checked out")) }, SpecTimeout(time.Second * 5)) It("lets the user place a hold and get notified later", func(ctx SpecContext) { Expect(valjean.Hold(ctx, library, "Les Miserables")).To(Succeed()) Expect(valjean.Holds(ctx)).To(ContainElement(book)) By("when Javert returns the book") Expect(javert.Return(ctx, library, book)).To(Succeed()) By("it eventually informs Valjean") notification := "Les Miserables is ready for pick up" Eventually(ctx, valjean.Notifications).Should(ContainElement(notification)) Expect(valjean.Checkout(ctx, library, "Les Miserables")).To(Succeed()) Expect(valjean.Books(ctx)).To(ContainElement(book)) Expect(valjean.Holds(ctx)).To(BeEmpty()) }, SpecTimeout(time.Second * 10)) }) }) When("the library does not have the book in question", func() { It("tells the reader the book is unavailable", func(ctx SpecContext) { err := valjean.Checkout(ctx, library, "Les Miserables") Expect(err).To(MatchError("Les Miserables is not in the library catalog")) }, SpecTimeout(time.Second * 5)) }) }) ``` -------------------------------- ### Install task Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Installs the task executable, a task runner that simplifies build and development workflows. Ensure Go is installed and configured. ```bash # https://taskfile.dev/installation/ $ go install github.com/go-task/task/v3/cmd/task@latest ``` -------------------------------- ### Initialize Root Logger Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-logr/logr/README.md Demonstrates the initial setup of a logr.Logger using a specific implementation like 'logimpl'. This is typically done early in the application's lifecycle. ```go func main() { // ... other setup code ... // Create the "root" logger. We have chosen the "logimpl" implementation, // which takes some initial parameters and returns a logr.Logger. logger := logimpl.New(param1, param2) // ... other setup code ... } ``` -------------------------------- ### Install Ginkgo Locally Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md Installs the Ginkgo binary locally using Go modules. This is a prerequisite for running tests and other development commands. ```bash go install ./... ``` -------------------------------- ### Blank Import Checker Example Source: https://github.com/antonboom/testifylint/blob/master/README.md Demonstrates the 'blank-import' checker. The '❌' example shows a common pattern of unused testify imports, while the '✅' example shows the corrected version with no unused imports. ```go ❌ import ( "testing" _ "github.com/stretchr/testify" _ "github.com/stretchr/testify/assert" _ "github.com/stretchr/testify/http" _ "github.com/stretchr/testify/mock" _ "github.com/stretchr/testify/require" _ "github.com/stretchr/testify/suite" ) ✅ import ( "testing" ) ``` -------------------------------- ### Basic Mock Expectation Setup Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Set up expectations on a mock object for a specific method call with given arguments and return values. Assert that these expectations are met after calling the code under test. ```go // TestSomething is an example of how to use our test object to // make assertions about some target code we are testing. func TestSomething(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations testObj.On("DoSomething", 123).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) } ``` -------------------------------- ### Basic Assertion with Testify Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Import the `testify/assert` package to use its assertion functions. This example shows a simple `assert.True` call. ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { assert.True(t, true, "True is true!") } ``` -------------------------------- ### Chained Warnings in Testify Linter Source: https://github.com/antonboom/testifylint/blob/master/README.md Demonstrates a sequence of linter warnings for a single Go code block, showing how the linter might flag multiple issues sequentially. This example illustrates the evolution of checks from simple comparisons to specific error assertions. ```go assert.True(err == nil) // compares: use assert.Equal assert.Equal(t, err, nil) // error-nil: use assert.NoError assert.NoError(t, err) // require-error: for error assertions use require require.NoError(t, err) ``` -------------------------------- ### Update Objx Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/objx/README.md Update the Objx package to the latest version using the `go get -u` command. ```go go get -u github.com/stretchr/objx ``` -------------------------------- ### Define Test Suite Structure Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Organize tests within a struct that embeds suite.Suite. This allows for shared setup, teardown, and test methods within a single test suite. ```go // Basic imports import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) // Define the suite, and absorb the built-in basic suite // functionality from testify - including a T() method which // returns the current testing context type ExampleTestSuite struct { suite.Suite VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test func (suite *ExampleTestSuite) SetupTest() { suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. func (suite *ExampleTestSuite) TestExample() { assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestExampleTestSuite(t *testing.T) { suite.Run(t, new(ExampleTestSuite)) } ``` -------------------------------- ### Access Data with Get and Type Assertions Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/objx/README.md Use the `Get` method to retrieve values from an `objx.Map`. Chain `Str()`, `Int()`, or other type-specific methods to extract the value. A default value can be provided. ```go name := m.Get("name").Str() age := m.Get("age").Int() nickname := m.Get("nickname").Str(name) ``` -------------------------------- ### Preview Documentation Changes Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md Serves the Ginkgo documentation locally using Jekyll. This allows you to preview your documentation updates before committing them. ```bash bundle && bundle exec jekyll serve ``` -------------------------------- ### Basic Assertions with Testify Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Demonstrates basic assertion methods like Equal, NotEqual, Nil, and NotNil from the assert package. Each assertion returns a boolean indicating success, which can be used for conditional testing. ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { // assert equality assert.Equal(t, 123, 123, "they should be equal") // assert inequality assert.NotEqual(t, 123, 456, "they should not be equal") // assert for nil (good for errors) assert.Nil(t, object) // assert for not nil (good when you expect something) if assert.NotNil(t, object) { // now we know that object isn't nil, we are safe to make // further assertions without causing any errors assert.Equal(t, "Something", object.Value) } } ``` -------------------------------- ### Testify assertion API comparison Source: https://github.com/antonboom/testifylint/blob/master/README.md Illustrates the difference between standard and f-assertion functions in the testify library, highlighting the historical context of their introduction. ```go func Equal(t TestingT, expected, actual any, msgAndArgs ...any) bool func Equalf(t TestingT, expected, actual any, msg string, args ...any) bool ``` ```go func Equal(t TestingT, expected, actual any) bool func Equalf(t TestingT, expected, actual any, msg string, args ...any) bool ``` -------------------------------- ### Proper Float Comparison Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Demonstrates correct ways to compare floating-point numbers and their containers. For direct comparison, use assert.InEpsilon or assert.InDelta. For containers, compare individual elements or use specialized slice functions. ```go ❌ assert.NotEqual(t, 42.42, a) assert.Greater(t, a, 42.42) assert.GreaterOrEqual(t, a, 42.42) assert.Less(t, a, 42.42) assert.LessOrEqual(t, a, 42.42) assert.True(t, a != 42.42) // assert.False(t, a == 42.42) assert.True(t, a > 42.42) // assert.False(t, a <= 42.42) assert.True(t, a >= 42.42) // assert.False(t, a < 42.42) assert.True(t, a < 42.42) // assert.False(t, a >= 42.42) assert.True(t, a <= 42.42) // assert.False(t, a > 42.42) ``` ```go type Tx struct { ID string Score float64 } ❌ assert.Equal(t, Tx{ID: "xxx", Score: 0.9643}, tx) ✅ assert.Equal(t, "xxx", tx.ID) assert.InEpsilon(t, 0.9643, tx.Score, 0.0001) ``` -------------------------------- ### Use appropriate testify APIs for comparisons Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces manual comparison logic with more idiomatic testify assertion functions like `Equal`, `Greater`, `Less`, etc. This improves clarity and failure messages. It also handles `time.Time` comparisons effectively. ```go ❌ assert.True(t, a == b) assert.True(t, a != b) assert.True(t, a > b) assert.True(t, a >= b) assert.True(t, a < b) assert.True(t, a <= b) assert.False(t, a == b) // And so on... ✅ assert.Equal(t, a, b) assert.NotEqual(t, a, b) assert.Greater(t, a, b) assert.GreaterOrEqual(t, a, b) assert.Less(t, a, b) assert.LessOrEqual(t, a, b) ``` ```go ❌ assert.True(t, t1.After(t2)) assert.Greater(t, t1.Compare(t2), 0) ✅ assert.Greater(t, t1, t2) ``` -------------------------------- ### Commit, Push, and Create GitHub Release Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/RELEASING.md Commands to commit changes, push to the remote repository, and create a new GitHub release using the GitHub CLI. ```bash git commit -m "vM.m.p" git push gh release create "vM.m.p" git fetch --tags origin master ``` -------------------------------- ### Migrate mockery to Expecter Structs Source: https://github.com/antonboom/testifylint/blob/master/README.md Migrate from string-based `testify/mock` expectations (using `m.On`) to generated Expecter Structs (using `m.EXPECT`). This provides compile-time verification of method names and signatures, reducing runtime errors. ```go ❌ m.On("CreateUser", mock.Anything, User{}).Return(nil) m.On("CountUsers").Return(123) ``` ```go ✅ m.EXPECT().CreateUser(mock.Anything, User{}).Return(nil) m.EXPECT().CountUsers().Return(123) ``` -------------------------------- ### Create objx.Map from JSON Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/objx/README.md Use `objx.FromJSON` to create an `objx.Map` from a JSON string. Error handling is included. ```go m, err := objx.FromJSON(json) ``` -------------------------------- ### Use Suite's Run for Subtests Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces `t.Run()` with `s.Run()` for running subtests within a suite. This ensures proper initialization of the test suite for subtests and prevents undefined behavior. ```go func (s *MySuite) TestSomething() { ❌ s.T().Run("subtest", func(t *testing.T) { assert.Equal(t, 42, result) }) ✅ s.Run("subtest", func() { s.Equal(42, result) }) } ``` -------------------------------- ### System Call Entry Points Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/golang.org/x/sys/unix/README.md Defines the entry points for system calls in assembly files. Use these for standard system calls or low-level operations. ```go func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) ``` -------------------------------- ### Unmarshal and Marshal YAML Data in Go Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/gopkg.in/yaml.v3/README.md Demonstrates how to unmarshal YAML data into a Go struct and a map, and then marshal them back into YAML format. Ensure struct fields are public for unmarshalling. ```Go package main import ( "fmt" "log" "gopkg.in/yaml.v3" ) var data = ` a: Easy! b: c: 2 d: [3, 4] ` // Note: struct fields must be public in order for unmarshal to // correctly populate the data. type T struct { A string B struct { RenamedC int `yaml:"c"` D []int `yaml:",flow"` } } func main() { t := T{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t:\n%v\n\n", t) d, err := yaml.Marshal(&t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t dump:\n%s\n\n", string(d)) m := make(map[interface{}]interface{}) err = yaml.Unmarshal([]byte(data), &m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m:\n%v\n\n", m) d, err = yaml.Marshal(&m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m dump:\n%s\n\n", string(d)) } ``` -------------------------------- ### Create objx.Map from JSON (Must prefix) Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/objx/README.md Use `objx.MustFromJSON` to create an `objx.Map` from a JSON string. This function will panic if an error occurs. ```go m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) ``` -------------------------------- ### Fix TestifyLint Issues Source: https://github.com/antonboom/testifylint/blob/master/README.md Automatically fix linting issues with the --fix flag. Remember to run 'go fmt' afterwards as unused imports may remain. ```bash $ testifylint --fix ./... ``` -------------------------------- ### Migrate klog.Infof to logr Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-logr/logr/README.md Convert klog format string logging to logr's structured key-value pairs. Use the format string's specifiers as keys and their corresponding variables as values. ```go logger.Error(err, "client returned an error", "code", responseCode) ``` ```go logger.V(4).Info("got a retry-after response when requesting url", "attempt", retries, "after seconds", seconds, "url", url) ``` -------------------------------- ### Simplify HTTP Status Code Assertions Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Use assert.HTTPSuccess, assert.HTTPError, or assert.HTTPRedirect for cleaner HTTP status code checks instead of assert.HTTPStatusCode. ```go assert.HTTPStatusCode(t, handler, http.MethodGet, "/index", nil, http.StatusOK) assert.HTTPStatusCode(t, handler, http.MethodGet, "/admin", nil, http.StatusNotFound) assert.HTTPStatusCode(t, handler, http.MethodGet, "/oauth", nil, http.StatusFound) // etc. ``` ```go assert.HTTPSuccess(t, handler, http.MethodGet, "/index", nil) assert.HTTPError(t, handler, http.MethodGet, "/admin", nil) assert.HTTPRedirect(t, handler, http.MethodGet, "/oauth", nil) ``` -------------------------------- ### Run Ginkgo Specs in Parallel Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/README.md Use the -p flag to enable parallel execution of Ginkgo specs. This is useful for large integration suites. ```bash ginkgo -p ``` -------------------------------- ### Use assert.ErrorIs and assert.ErrorAs for Error Checking Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces generic error checking functions like `errors.Is`, `errors.As`, and `assert.Error` with the more specific `testify` assertions `assert.ErrorIs` and `assert.ErrorAs`. This improves clarity, handles error wrapping correctly, and provides better failure messages. It also incorporates checks similar to `go vet`'s `errorsas`. ```go ❌ assert.Error(t, err, errSentinel) // Typo, errSentinel hits `msgAndArgs`. assert.NoError(t, err, errSentinel) assert.IsType(t, err, errSentinel) assert.IsType(t, (*http.MaxBytesError)(nil), err) assert.IsNotType(t, err, errSentinel) assert.IsNotType(t, store.NotFoundError{}, err) assert.True(t, errors.Is(err, errSentinel)) assert.False(t, errors.Is(err, errSentinel)) assert.True(t, errors.As(err, &target)) assert.False(t, errors.As(err, &target)) ``` ```go ✅ assert.ErrorIs(t, err, errSentinel) assert.NotErrorIs(t, err, errSentinel) assert.ErrorAs(t, err, &target) assert.NotErrorAs(t, err, &target) ``` ```go assert.ErrorAs(t, err, new(*http.MaxBytesError)) assert.ErrorAs(t, err, store.NotFoundError{}) ``` ```go mbErr := new(http.MaxBytesError) require.ErrorAs(t, err, &mbErr) assert.Equal(t, 100, mbErr.Limit) ``` ```go if mbErr := new(http.MaxBytesError); assert.ErrorAs(t, err, &mbErr) { assert.Equal(t, 100, mbErr.Limit) } ``` -------------------------------- ### Enable All TestifyLint Checkers Source: https://github.com/antonboom/testifylint/blob/master/README.md Enable all available checkers for comprehensive linting. This command checks all Go files in the current module. ```bash # Enable all checkers. $ testifylint --enable-all ./... ``` -------------------------------- ### Use Standard Library HTTP Constants Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Replace magic numbers for HTTP status codes and methods with constants from the Go standard library's `net/http` package for better readability and maintainability. ```go ❌ assert.HTTPStatusCode(t, handler, "GET", "/index", nil, 200) assert.HTTPBodyContains(t, handler, "GET", "/index", nil, "counter") // etc. ✅ assert.HTTPStatusCode(t, handler, http.MethodGet, "/index", nil, http.StatusOK) assert.HTTPBodyContains(t, handler, http.MethodGet, "/index", nil, "counter") ``` -------------------------------- ### Mock Handler Cleanup and Re-registration Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Demonstrates how to unset a previously registered mock handler using Unset() and then register a new one. This allows for changing mock behavior mid-test or for specific scenarios. ```go // TestSomethingElse2 is a third example that shows how you can use // the Unset method to cleanup handlers and then add new ones. func TestSomethingElse2(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations with a placeholder in the argument list mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) // remove the handler now so we can add another one that takes precedence mockCall.Unset() // return false now instead of true testObj.On("DoSomething", mock.Anything).Return(false, nil) testObj.AssertExpectations(t) } ``` -------------------------------- ### Pass Logger to Application Object Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-logr/logr/README.md Shows how to pass the initialized logr.Logger to other parts of the application, such as an application object, for use in its methods. ```go app := createTheAppObject(logger) app.Run() ``` -------------------------------- ### Run Task Command Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Executes the main task command to perform various development tasks including tidying, formatting, linting, generating tests, and running tests. This is a comprehensive command for development workflow. ```bash $ task Tidy... Fmt... Lint... Generate analyzer tests... Test... Install... ``` -------------------------------- ### Chained Assertions with Testify Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Shows how to use the assert.New(t) method to create an assertion object for chaining multiple assertions without passing the testing.T object repeatedly. This can lead to more concise test code. ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { assert := assert.New(t) // assert equality assert.Equal(123, 123, "they should be equal") // assert inequality assert.NotEqual(123, 456, "they should not be equal") // assert for nil (good for errors) assert.Nil(object) // assert for not nil (good when you expect something) if assert.NotNil(object) { // now we know that object isn't nil, we are safe to make // further assertions without causing any errors assert.Equal("Something", object.Value) } } ``` -------------------------------- ### Correct Suite Method Signatures Source: https://github.com/antonboom/testifylint/blob/master/README.md Ensures test methods in suites are clean and do not accept arguments or return values. Helper methods should be private and use `s.T().Helper()`. ```go ❌ func (s *MySuite) SetupTest(i int) { /* ... */ } func (s *MySuite) TestAlice(t *testing.T) { s.SetupTest(rand()) /* ... */ } func (s *MySuite) TestBob() { s.SetupTest(rand()) /* ... */ } ✅ func (s *MySuite) SetupTest() { s.setupTest(rand()) } // Use inversion-of-control's methods from suite interfaces. func (s *MySuite) TestAlice() { /* ... */ } func (s *MySuite) TestBob() { /* ... */ } func (s *MySuite) setupTest(i int) { /* ... */ } ``` -------------------------------- ### Mock Expectation with Placeholder Argument Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Use mock.Anything as a placeholder for arguments when the exact value is not important or cannot be predicted. This is useful for dynamically generated arguments. ```go // TestSomethingWithPlaceholder is a second example of how to use our test object to // make assertions about some target code we are testing. // This time using a placeholder. Placeholders might be used when the // data being passed in is normally dynamically generated and cannot be // predicted beforehand (eg. containing hashes that are time sensitive) func TestSomethingWithPlaceholder(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations with a placeholder in the argument list testObj.On("DoSomething", mock.Anything).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) } ``` -------------------------------- ### Use assert.Empty and assert.NotEmpty for Zero/Len Checks Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces verbose checks for zero length or zero values with the more idiomatic assert.Empty and assert.NotEmpty. This rule is enabled by default and aims to improve clarity and failure messages. ```go ❌ assert.Len(t, arr, 0) assert.Zero(t, str) assert.Zero(t, len(arr)) assert.Equal(t, 0, len(arr)) assert.EqualValues(t, 0, len(arr)) assert.Exactly(t, 0, len(arr)) assert.LessOrEqual(t, len(arr), 0) assert.GreaterOrEqual(t, 0, len(arr)) assert.Less(t, len(arr), 1) assert.Greater(t, 1, len(arr)) assert.Equal(t, "", str) assert.EqualValues(t, "", str) assert.Exactly(t, "", str) assert.Equal(t, ``, str) assert.EqualValues(t, ``, str) assert.Exactly(t, ``, str) assert.Positive(t, len(arr)) assert.NotZero(t, str) assert.NotZero(t, len(arr)) assert.NotEqual(t, 0, len(arr)) assert.NotEqualValues(t, 0, len(arr)) assert.Greater(t, len(arr), 0) assert.Less(t, 0, len(arr)) assert.NotEqual(t, "", str) assert.NotEqualValues(t, "", str) assert.NotEqual(t, ``, str) assert.NotEqualValues(t, ``, str) ``` ```go ✅ assert.Empty(t, arr) assert.NotEmpty(t, arr) ``` -------------------------------- ### Avoid Using Package Assertions in Suites Source: https://github.com/antonboom/testifylint/blob/master/README.md Prefer using suite methods like `s.Equal()` over package-level `assert.Equal()` within test suites for better integration and consistency. ```go func (s *MySuite) TestSomething() { ❌ assert.Equal(s.T(), 42, value) ✅ s.Equal(42, value) } ``` -------------------------------- ### Generate and Run Tests Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Executes the task runner to generate analyzer tests and run the existing test suite. Expect tests to fail initially, indicating the need for checker implementation. ```bash $ task test Generate analyzer tests... Test... ... --- FAIL: TestTestifyLint_CheckersDefault FAIL ``` -------------------------------- ### Log within Application Object Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-logr/logr/README.md Illustrates how an application object uses its stored logr.Logger to log informational messages with key-value pairs. ```go type appObject struct { // ... other fields ... logger logr.Logger // ... other fields ... } func (app *appObject) Run() { app.logger.Info("starting up", "timestamp", time.Now()) // ... app code ... } ``` -------------------------------- ### Simplify Regexp Assertions in Testify Source: https://github.com/antonboom/testifylint/blob/master/README.md Use the simplified `assert.Regexp` and `assert.NotRegexp` by passing the regex string directly, avoiding the need to pre-compile it with `regexp.MustCompile`. ```go ❌ assert.Regexp(t, regexp.MustCompile(`[.*] DEBUG (.*TestNew.*): message`), out) assert.NotRegexp(t, regexp.MustCompile(`[.*] TRACE message`), out) ✅ assert.Regexp(t, `[.*] DEBUG (.*TestNew.*): message`, out) assert.NotRegexp(t, `[.*] TRACE message`, out) ``` -------------------------------- ### Use InEpsilon or InDelta for float comparisons Source: https://github.com/antonboom/testifylint/blob/master/README.md This rule flags direct equality checks for floating-point numbers due to potential rounding issues. It recommends using `assert.InEpsilon` or `assert.InDelta` for safer comparisons. Autofix is disabled. ```go ❌ assert.Equal(t, 42.42, result) assert.EqualValues(t, 42.42, result) assert.Exactly(t, 42.42, result) assert.True(t, result == 42.42) assert.False(t, result != 42.42) ✅ assert.InEpsilon(t, 42.42, result, 0.0001) // Or assert.InDelta ``` -------------------------------- ### Use assert.JSONEq and assert.YAMLEq for Encoded Comparisons Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces generic equality assertions for JSON or YAML strings with specialized `testify` functions like `assert.JSONEq` and `assert.YAMLEq`. This provides better error messages and handles nuances of the formats. It also removes unnecessary conversions. ```go ❌ assert.Equal(t, `{"foo": "bar"}`, body) assert.EqualValues(t, `{"foo": "bar"}`, body) assert.Exactly(t, `{"foo": "bar"}`, body) assert.Equal(t, expectedJSON, resultJSON) assert.Equal(t, expBodyConst, w.Body.String()) assert.Equal(t, fmt.Sprintf(`{"value":"%s"}`, hexString), result) assert.Equal(t, "{}", json.RawMessage(resp)) assert.Equal(t, expJSON, strings.Trim(string(resultJSONBytes), "\n")) // + Replace, ReplaceAll, TrimSpace assert.Equal(t, expectedYML, conf) ``` ```go ✅ assert.JSONEq(t, `{"foo": "bar"}`, body) assert.YAMLEq(t, expectedYML, conf) ``` -------------------------------- ### Load Slim-Sprig FuncMap in Go Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-task/slim-sprig/v3/README.md Load the Slim-Sprig FuncMap before parsing templates. This ensures all custom functions are available when templates are processed. ```go import ( "html/template" "github.com/go-task/slim-sprig" ) // This example illustrates that the FuncMap *must* be set before the // templates themselves are loaded. tpl := template.Must( template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html") ) ``` -------------------------------- ### Use require.Len Before Slice Indexing Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Ensure a length constraint is checked with require.Len before accessing slice elements to prevent panics. ```go assert.Len(t, arr, 3) // Or assert.NotEmpty(t, arr) and other variations. assert.Equal(t, "gopher", arr[1]) ``` ```go require.Len(t, arr, 3) // Or require.NotEmpty(t, arr) and other variations. assert.Equal(t, "gopher", arr[1]) ``` -------------------------------- ### Use fmt.Sprintf in logr values Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/go-logr/logr/README.md If a format string is absolutely necessary, use fmt.Sprintf within a key's value. This approach should be used sparingly. ```go logger.Info("unable to reflect over type", "type", fmt.Sprintf("%T")) ``` -------------------------------- ### Handle Indirect Callbacks in Go-Require Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Demonstrates how to support indirect callbacks in go-require and require-error checkers, especially when an error is not expected from require-error. ```go callback := func() { // Error from `go-require` needed. assert.Error(t, nil) // But no error from `require-error` is expected. assert.Error(t, nil) } wg.Go(callback) go callback() ``` -------------------------------- ### Vet Go Code Changes Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md Runs the Go vet tool on your changes to detect suspicious constructs. This helps identify potential errors and enforce code quality. ```bash go vet ./... ``` -------------------------------- ### Simplify Slice Comparison with ElementsMatch Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Use assert.ElementsMatch for comparing slices when order doesn't matter. This replaces manual sorting and element-wise comparison. ```go ❌ require.Equal(t, len(expected), len(result)) sort.Slice(expected, /* ... */) sort.Slice(result, /* ... */) for i := range result { assert.Equal(t, expected[i], result[i]) } // Or for Go >= 1.21 slices.Sort(expected) slices.Sort(result) assert.True(t, slices.Equal(expected, result)) ✅ assert.ElementsMatch(t, expected, result) ``` -------------------------------- ### Avoid Require in Cleanup Functions Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Warns about using require in t.Cleanup functions and suite teardown methods to prevent potential resource leaks, as require can terminate the current goroutine. ```go func (s *ServiceIntegrationSuite) TearDownTest() { if p := s.verdictsProducer; p != nil { s.Require().NoError(p.Close()) ❌ } if c := s.dlqVerdictsConsumer; c != nil { s.NoError(c.Close()) } s.DBSuite.TearDownTest() s.ks.TearDownTest() } ``` -------------------------------- ### Simplify Extra Assert Calls in Suites Source: https://github.com/antonboom/testifylint/blob/master/README.md Removes unnecessary `s.Assert()` calls when a direct method like `s.Equal()` is available. Can be configured to enforce consistency with `s.Assert()` and `s.Require()`. ```go func (s *MySuite) TestSomething() { ❌ s.Assert().Equal(42, value) ✅ s.Equal(42, value) } ``` ```go func (s *MySuite) TestSomething() { // ... ❌ s.Require().NoError(err) s.Equal(42, value) ✅ s.Require().NoError(err) s.Assert().Equal(42, value) } ``` -------------------------------- ### Define Mock Object and Method Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/stretchr/testify/README.md Define a mock object that embeds mock.Mock and implements methods to record calls and return predefined arguments. This is useful for stubbing dependencies in your tests. ```go package yours import ( t"testing" "github.com/stretchr/testify/mock" ) /* Test objects */ // MyMockedObject is a mocked object that implements an interface // that describes an object that the code I am testing relies on. type MyMockedObject struct { mock.Mock } // DoSomething is a method on MyMockedObject that implements some interface // and just records the activity, and returns what the Mock object tells it to. // // In the real object, this method would do something useful, but since this // is a mocked object - we're just going to stub it out. // // NOTE: This method is not being tested here, code that uses this object is. func (m *MyMockedObject) DoSomething(number int) (bool, error) { args := m.Called(number) return args.Bool(0), args.Error(1) } ``` -------------------------------- ### Detect Flaky Time Assertions Source: https://github.com/antonboom/testifylint/blob/master/README.md Avoid equality-based assertions on time.Time values as they can be flaky due to internal state like monotonic clock readings. Prefer specific comparison methods like WithinDuration, Equal, Unix, UTC, or Round. ```go ❌ assert.Equal(t, expTime, actualTime) assert.EqualValues(t, expTime, actualTime) assert.Exactly(t, expTime, actualTime) assert.NotEqual(t, expTime, actualTime) assert.NotEqualValues(t, expTime, actualTime) ``` ```go ✅ assert.WithinDuration(t, t1, t2, 0) // The most readable error message. assert.True(t, t1.Equal(t2)) assert.Equal(t, t1.Unix(), t2.Unix()) // Or UnixMilli, UnixMicro, UnixNano. assert.Equal(t, t1.UTC(), t2.UTC()) // Or Local. assert.Equal(t, t1.Round(0), t2.Round(0)) // Or Truncate. ... ``` -------------------------------- ### Configure Go Require Checker Source: https://github.com/antonboom/testifylint/blob/master/README.md Configure the 'go-require' checker to ignore specific HTTP handler patterns. This is useful for excluding certain types of HTTP handlers from checks. ```bash $ testifylint --go-require.ignore-http-handlers ./... ``` -------------------------------- ### Simplify string and slice containment checks Source: https://github.com/antonboom/testifylint/blob/master/README.md Replaces manual checks using `strings.Contains` or slice indexing with testify's `Contains` and `NotContains` functions. This rule also simplifies checks for subsets and non-subsets in slices. ```go ❌ assert.True(t, strings.Contains(a, "abc123")) assert.False(t, !strings.Contains(a, "abc123")) assert.False(t, strings.Contains(a, "abc123")) assert.True(t, !strings.Contains(a, "abc123")) assert.Contains(t, arr, 1, 2) assert.NotContains(t, arr, 1, 2) ✅ assert.Contains(t, a, "abc123") assert.NotContains(t, a, "abc123") assert.Subset(t, arr, 1, 2) assert.NotSubset(t, arr, 1, 2) ``` ```go ❌ assert.Contains(t, cadvisorInfoToUserDefinedMetrics(&cInfo), statsapi.UserDefinedMetric{ UserDefinedMetricDescriptor: statsapi.UserDefinedMetricDescriptor{ Name: "qos", Type: statsapi.MetricGauge, Units: "per second", }, Time: metav1.NewTime(timestamp2), Value: 100, }, statsapi.UserDefinedMetric{ UserDefinedMetricDescriptor: statsapi.UserDefinedMetricDescriptor{ Name: "cpuLoad", Type: statsapi.MetricCumulative, Units: "count", }, Time: metav1.NewTime(timestamp2), Value: 2.1, }) ✅ assert.Subset(t, cadvisorInfoToUserDefinedMetrics(&cInfo), []statsapi.UserDefinedMetric{ { UserDefinedMetricDescriptor: statsapi.UserDefinedMetricDescriptor{ Name: "qos", Type: statsapi.MetricGauge, Units: "per second", }, Time: metav1.NewTime(timestamp2), Value: 100, }, { UserDefinedMetricDescriptor: statsapi.UserDefinedMetricDescriptor{ Name: "cpuLoad", Type: statsapi.MetricCumulative, Units: "count", }, Time: metav1.NewTime(timestamp2), Value: 2.1, }}) ``` ```go ❌ logLines := getLogLines(t, ql1File) /* { "time": "2025-09-14T07:49:51.652292+03:00", "params": { "query": "test statement" }, "error": "failure", "spanID": "0000000000000000", "foo": "bar" } */ require.Contains(t, logLines[0], "error", errors.New("failure")) require.Contains(t, logLines[0], "params", map[string]interface{}{"query": "test statement"}) ✅ logLines := getLogLines(t, ql1File) require.Contains(t, logLines[0], "\"error\":\"failure\"") require.Contains(t, logLines[0], "\"params\":{\"query\":\"test statement\"}") ``` -------------------------------- ### Create Checker Skeleton Source: https://github.com/antonboom/testifylint/blob/master/CONTRIBUTING.md Defines the basic structure for a new checker in Go. This includes the package, type definition, constructor, and name method, satisfying the checkers.Checker interface. ```go package checkers // TimeCompare detects situations like // // assert.Equal(t, expTs, actualTs) // // and requires // // assert.True(t, actualTs.Equal(expTs)) type TimeCompare struct{} // NewTimeCompare constructs TimeCompare checker. func NewTimeCompare() TimeCompare { return TimeCompare{} } func (TimeCompare) Name() string { return "time-compare" } ``` -------------------------------- ### Run Ginkgo Tests Source: https://github.com/antonboom/testifylint/blob/master/analyzer/testdata/src/vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.md Executes all Ginkgo tests recursively and in parallel. This command verifies that your changes have not introduced regressions. ```bash ginkgo -r -p ``` -------------------------------- ### Mark Test Helper Methods with s.T().Helper() Source: https://github.com/antonboom/testifylint/blob/master/README.md Adds `s.T().Helper()` to test helper methods within suites. This is primarily for consistency with non-suite test helpers and explicitly marks the method as a test helper. ```go ❌ func (s *RoomSuite) assertRoomRound(roundID RoundID) { s.Equal(roundID, s.getRoom().CurrentRound.ID) } ✅ func (s *RoomSuite) assertRoomRound(roundID RoundID) { s.T().Helper() s.Equal(roundID, s.getRoom().CurrentRound.ID) } ``` -------------------------------- ### Use assert.Len for Length Assertions Source: https://github.com/antonboom/testifylint/blob/master/README.md Prefer `assert.Len` over `assert.Equal`, `assert.EqualValues`, `assert.Exactly`, or `assert.True` when checking the length of a slice or array. This provides a clearer failure message and uses a more appropriate API. ```go ❌ assert.Equal(t, 42, len(arr)) assert.Equal(t, len(arr), 42) assert.EqualValues(t, 42, len(arr)) assert.EqualValues(t, len(arr), 42) assert.Exactly(t, 42, len(arr)) assert.Exactly(t, len(arr), 42) assert.True(t, 42 == len(arr)) assert.True(t, len(arr) == 42) assert.Equal(t, value, len(arr)) assert.EqualValues(t, value, len(arr)) assert.Exactly(t, value, len(arr)) assert.True(t, len(arr) == value) assert.Equal(t, len(expArr), len(arr)) assert.EqualValues(t, len(expArr), len(arr)) assert.Exactly(t, len(expArr), len(arr)) assert.True(t, len(arr) == len(expArr)) ``` ```go ✅ assert.Len(t, arr, 42) assert.Len(t, arr, value) assert.Len(t, arr, len(expArr)) ``` -------------------------------- ### Detect Zero Time Assertions Source: https://github.com/antonboom/testifylint/blob/master/README.md This checker identifies assertions comparing time.Time values against zero values. It recommends using the more appropriate testify/assert API (Zero, NotZero) for clearer failure messages. ```go ❌ assert.Equal(t, time.Time{}, ts) assert.EqualValues(t, time.Time{}, ts) assert.Exactly(t, time.Time{}, ts) assert.True(t, ts.IsZero()) assert.True(t, ts.Equal(time.Time{})) assert.NotEqual(t, time.Time{}, ts) assert.NotEqualValues(t, time.Time{}, ts) assert.False(t, ts.IsZero()) assert.False(t, ts.Equal(time.Time{})) ``` ```go ✅ assert.Zero(t, ts) assert.NotZero(t, ts) ``` -------------------------------- ### Use Negative/Positive Assertions in Testify Source: https://github.com/antonboom/testifylint/blob/master/README.md Prefer `assert.Negative` and `assert.Positive` over manual comparisons for clearer failure messages and more appropriate API usage. Supports typed zeros. ```go ❌ assert.Less(t, a, 0) assert.Greater(t, 0, a) assert.True(t, a < 0) assert.True(t, 0 > a) assert.False(t, a >= 0) assert.False(t, 0 <= a) assert.Greater(t, a, 0) assert.Less(t, 0, a) assert.True(t, a > 0) assert.True(t, 0 < a) assert.False(t, a <= 0) assert.False(t, 0 >= a) ✅ assert.Negative(t, a) assert.Positive(t, a) ``` -------------------------------- ### Use Nil/NotNil Assertions in Testify Source: https://github.com/antonboom/testifylint/blob/master/README.md Use `assert.Nil` and `assert.NotNil` for comparing with nil, as `assert.Equal` with untyped nil can lead to unexpected behavior, especially with non-interface types or function types. ```go ❌ assert.Equal(t, nil, value) assert.EqualValues(t, nil, value) assert.Exactly(t, nil, value) assert.NotEqual(t, nil, value) assert.NotEqualValues(t, nil, value) ✅ assert.Nil(t, value) assert.NotNil(t, value) ``` ```go assert.Equal(t, nil, eventsChan) // Always fail. assert.NotEqual(t, nil, eventsChan) // Always pass. ``` ```go assert.Equal(t, (chan Event)(nil), eventsChan) assert.NotEqual(t, (chan Event)(nil), eventsChan) ```