The Go linter paralleltest
checks that the t.Parallel gets called for the test method and for the range of test cases within the test.
go install github.com/kunwardeep/paralleltest@latest
paralleltest ./...
A few options can be activated by flag:
-i
: Ignore missing calls tot.Parallel
and only report incorrect uses of it.-ignoremissingsubtests
: Require that top-level tests specifyt.Parallel
, but don't require it in subtests (t.Run(...)
).-ignoreloopVar
: Ignore loop variable detection.
- Go 1.23.0 or later
- Make
- Clone the repository:
git clone https://github.com/kunwardeep/paralleltest.git
cd paralleltest
- Install development tools:
make install_devtools
- Install dependencies:
make ensure_deps
- Run tests:
make test
- Run linter:
make lint
To fix linting issues automatically:
make lint_fix
The project uses GitHub Actions for continuous integration. The workflow includes:
- Running tests with race condition detection
- Running golangci-lint for code quality checks
The workflow runs on:
- Pull requests
- Pushes to the main branch
// bad
func TestFunctionMissingCallToParallel(t *testing.T) {
}
// good
func TestFunctionMissingCallToParallel(t *testing.T) {
t.Parallel()
// ^ call to t.Parallel()
}
// Error displayed
// Function TestFunctionMissingCallToParallel missing the call to method parallel
// bad
func TestFunctionRangeMissingCallToParallel(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fmt.Println(tc.name)
})
}
}
// good
func TestFunctionRangeMissingCallToParallel(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// ^ call to t.Parallel()
fmt.Println(tc.name)
})
}
}
// Error displayed
// Range statement for test TestFunctionRangeMissingCallToParallel missing the call to method parallel in t.Run
// bad
func TestFunctionRangeNotUsingRangeValueInTDotRun(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run("this is a test name", func(t *testing.T) {
// ^ call to tc.name missing
t.Parallel()
fmt.Println(tc.name)
})
}
}
// good
func TestFunctionRangeNotUsingRangeValueInTDotRun(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
fmt.Println(tc.name)
})
}
}
// Error displayed
// Range statement for test TestFunctionRangeNotUsingRangeValueInTDotRun does not use range value in t.Run
t.Parallel()
is called in the range method and test case variable tc being used, but is not reinitialised (More Info)
// bad
func TestFunctionRangeNotReInitialisingVariable(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
fmt.Println(tc.name)
})
}
}
// good
func TestFunctionRangeNotReInitialisingVariable(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
tc:=tc
// ^ tc variable reinitialised
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
fmt.Println(tc.name)
})
}
}
// Error displayed
// Range statement for test TestFunctionRangeNotReInitialisingVariable does not reinitialise the variable tc
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.