A Jest-like testing framework for Go with powerful hooks, nested describes, and an interactive watch mode.
✨ Expressive BDD-style syntax - Familiar Describe, It, BeforeAll, BeforeEach, AfterEach, and AfterAll hooks
🔗 Hook Inheritance - Hooks from parent Describe blocks are automatically inherited by nested blocks
🎯 Rich Expectations - Fluent API with Expect() and matchers like ToBe(), ToEqual(), ToContain(), and more
👀 Interactive Watch Mode - File watching with smart filtering and interactive controls
🎨 Beautiful Output - Color-coded test results with clear failure messages and code snippets
go get github.com/caiolandgraf/gest/v2Install the CLI tool:
go install github.com/caiolandgraf/gest/v2/cmd/gest@latestCreate a test file:
package mypackage
import (
"testing"
"github.com/caiolandgraf/gest/v2/gest"
)
func TestCalculator(t *testing.T) {
gest.Describe("Calculator").
It("adds numbers", func(t *gest.T) {
t.Expect(2 + 2).ToBe(4)
}).
It("subtracts numbers", func(t *gest.T) {
t.Expect(5 - 3).ToBe(2)
}).
Run(t)
}Run your tests:
gest ./...Run once before or after all tests in a suite:
func TestDatabase(t *testing.T) {
var db *Database
gest.Describe("Database").
BeforeAll(func(t *gest.T) {
db = connectToDatabase()
t.Expect(db).Not().ToBeNil()
}).
AfterAll(func(t *gest.T) {
db.Close()
}).
It("queries data", func(t *gest.T) {
result := db.Query("SELECT * FROM users")
t.Expect(result).Not().ToBeNil()
}).
Run(t)
}Run before or after each test in a suite:
func TestUserService(t *testing.T) {
var user *User
gest.Describe("UserService").
BeforeEach(func(t *gest.T) {
user = &User{Name: "Test User"}
}).
AfterEach(func(t *gest.T) {
user = nil
}).
It("creates a user", func(t *gest.T) {
t.Expect(user.Name).ToBe("Test User")
}).
It("updates a user", func(t *gest.T) {
user.Name = "Updated"
t.Expect(user.Name).ToBe("Updated")
}).
Run(t)
}Hooks from parent Describe blocks are automatically inherited by nested blocks:
func TestNestedHooks(t *testing.T) {
var outerSetup int
var innerSetup int
gest.Describe("Outer Suite").
BeforeEach(func(t *gest.T) {
outerSetup++ // Runs for ALL tests
}).
It("outer test", func(t *gest.T) {
t.Expect(outerSetup).ToBe(1)
}).
Describe("Inner Suite", func(s *gest.Suite) {
s.BeforeEach(func(t *gest.T) {
innerSetup++ // Runs only for inner tests
})
s.It("inner test", func(t *gest.T) {
// Parent BeforeEach ran first
t.Expect(outerSetup).ToBe(2)
// Then child BeforeEach ran
t.Expect(innerSetup).ToBe(1)
})
}).
Run(t)
}Hook execution order:
- Parent
BeforeAll→ ChildBeforeAll - Parent
BeforeEach→ ChildBeforeEach→ Test → ChildAfterEach→ ParentAfterEach - Child
AfterAll→ ParentAfterAll
Organize tests hierarchically with nested Describe blocks:
func TestCalculator(t *testing.T) {
gest.Describe("Calculator").
Describe("Addition", func(s *gest.Suite) {
s.It("adds positive numbers", func(t *gest.T) {
t.Expect(2 + 3).ToBe(5)
})
s.It("adds negative numbers", func(t *gest.T) {
t.Expect(-2 + -3).ToBe(-5)
})
}).
Describe("Multiplication", func(s *gest.Suite) {
s.It("multiplies positive numbers", func(t *gest.T) {
t.Expect(2 * 3).ToBe(6)
})
}).
Run(t)
}t.Expect(value).ToBe(expected) // Same reference (==)
t.Expect(value).ToEqual(expected) // Deep equality
t.Expect(value).ToBeNil() // Check for nil
t.Expect(value).ToBeTrue() // Boolean true
t.Expect(value).ToBeFalse() // Boolean falset.Expect(value).Not().ToBe(unexpected)
t.Expect(value).Not().ToBeNil()t.Expect(value).ToBeGreaterThan(5)
t.Expect(value).ToBeGreaterThanOrEqual(5)
t.Expect(value).ToBeLessThan(10)
t.Expect(value).ToBeLessThanOrEqual(10)
t.Expect(value).ToBeCloseTo(3.14, 0.01) // Within delta
t.Expect(value).ToBeNaN()t.Expect(slice).ToContain(item)
t.Expect(slice).ToHaveLength(5)t.Expect(value).ToBeInstanceOf(reflect.TypeOf(&User{}))t.Expect(func() { panic("error") }).ToThrow()t.Expect(user).ToMatchObject(map[string]interface{}{
"Name": "John",
"Age": 30,
})Start watch mode to automatically re-run tests when files change:
gest --watch ./...When in watch mode, press:
f- Run only failed testso- Run only tests related to changed filesp- Filter by filename regex patternt- Filter by test name regex patternc- Clear all filtersEnter- Trigger a test runq- Quit watch mode
Filter by filename:
Press p
> Filename regex pattern: calculator
Filter by test name:
Press t
> Test name regex pattern: Addition
gest [options] [packages...]
Options:
--watch, -w Enable watch mode
--coverage, -c Show coverage information
--no-cache Disable test caching
--debounce=80ms Debounce duration for file changes
-p pattern Filter by filename regex
# Pass additional flags to go test
-- Everything after this is passed to go testExamples:
# Run all tests
gest ./...
# Watch mode with coverage
gest --watch --coverage ./...
# Filter by filename pattern
gest -p "calculator" ./...
# Pass flags to go test
gest -- -race -timeout 30s ./...gest/
├── gest/
│ └── gest.go # Core testing library
├── cmd/
│ └── gest/
│ └── main.go # CLI tool with watch mode
└── example/
├── calculator_test.go
└── math_test.go
gest is built on top of Go's standard testing package. Each Describe suite is converted to a t.Run() subtest, which means:
- ✅ Works with all Go testing tools (
go test, IDEs, CI/CD) - ✅ Parallel test execution support (via
t.Parallel()) - ✅ Test filtering with
-runflag - ✅ Coverage reporting
- ✅ Benchmarking support
All methods return *Suite for fluent chaining:
gest.Describe("Suite").
BeforeAll(setup).
BeforeEach(prepare).
It("test 1", test1).
It("test 2", test2).
AfterEach(cleanup).
AfterAll(teardown).
Run(t)gest.Describe("Level 1").
Describe("Level 2", func(s *gest.Suite) {
s.Describe("Level 3", func(s *gest.Suite) {
s.It("deeply nested test", func(t *gest.T) {
// All parent hooks run before this
})
})
}).
Run(t)| Feature | Jest (JavaScript) | gest (Go) |
|---|---|---|
describe() |
✅ | ✅ Describe() |
it() / test() |
✅ | ✅ It() |
beforeAll() |
✅ | ✅ BeforeAll() |
afterAll() |
✅ | ✅ AfterAll() |
beforeEach() |
✅ | ✅ BeforeEach() |
afterEach() |
✅ | ✅ AfterEach() |
| Nested describes | ✅ | ✅ |
| Hook inheritance | ✅ | ✅ |
| Watch mode | ✅ | ✅ |
| Matchers | ✅ | ✅ |
| Native integration | N/A | ✅ Go testing |
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
Inspired by Jest - the delightful JavaScript testing framework.