Ginkgo provides extension packages for advanced use cases, including global state management and formatting utilities.
The extensions/globals package provides an interface to alter the global state of a Ginkgo suite. This is not intended for normal use but can be useful in specific scenarios like testing frameworks or advanced tooling.
import "github.com/onsi/ginkgo/v2/extensions/globals"Resets the global state by creating a new Suite instance, removing all groups, tests, and blocks.
func Reset()Warning: This function is not intended for normal testing use. It completely resets Ginkgo's internal state, removing all registered specs, containers, and lifecycle hooks.
Use Cases:
Example:
import (
. "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo/v2/extensions/globals"
)
// Reset before defining new test structure
func SetupNewTestSuite() {
globals.Reset()
// Now define new specs from scratch
Describe("Fresh suite", func() {
It("has no previous state", func() {
// New test
})
})
}Important Notes:
Reset() if you fully understand its implicationsReset(), all previously defined specs are lostThe formatter package provides text formatting and colorization utilities used by Ginkgo's reporters.
import "github.com/onsi/ginkgo/v2/formatter"const COLS int = 80Default column width for formatted output.
var ColorableStdOut io.Writer
var ColorableStdErr io.Writer
var SingletonFormatter FormatterColorableStdOut: Colorable stdout writer (Windows-compatible)ColorableStdErr: Colorable stderr writer (Windows-compatible)SingletonFormatter: Default formatter instancetype ColorMode uint8
const (
ColorModeNone ColorMode = iota
ColorModeTerminal
ColorModePassthrough
)ColorModeNone: No color outputColorModeTerminal: Color output for terminalsColorModePassthrough: Pass through color codes without processingText formatter with color support and layout capabilities.
type Formatter struct {
ColorMode ColorMode
}Constructors:
func New(colorMode ColorMode) Formatter
func NewWithNoColorBool(noColor bool) FormatterMethods:
func (f Formatter) F(format string, args ...any) string
func (f Formatter) Fi(indentation uint, format string, args ...any) string
func (f Formatter) Fiw(
indentation uint,
maxWidth uint,
format string,
args ...any,
) string
func (f Formatter) CycleJoin(
elements []string,
joiner string,
cycle []string,
) stringPackage-Level Functions:
func F(format string, args ...any) string
func Fi(indentation uint, format string, args ...any) string
func Fiw(indentation uint, maxWidth uint, format string, args ...any) stringThe formatter supports special format codes for colors and styles:
{{/}}: Reset formatting{{bold}}: Bold text{{red}}: Red text{{green}}: Green text{{yellow}}: Yellow text{{blue}}: Blue text{{magenta}}: Magenta text{{cyan}}: Cyan text{{gray}}: Gray text{{light-gray}}: Light gray textExample:
import "github.com/onsi/ginkgo/v2/formatter"
// Basic formatting
msg := formatter.F("{{green}}Success{{/}}: Test passed")
// With indentation
msg := formatter.Fi(2, "{{red}}Error:{{/}} %s", errMsg)
// With indentation and word wrap
msg := formatter.Fiw(2, 80,
"{{yellow}}Warning:{{/}} This is a long warning message that will be wrapped")
// Cycle colors through elements
colors := []string{"{{cyan}}", "{{magenta}}", "{{yellow}}"}
msg := formatter.SingletonFormatter.CycleJoin(
[]string{"apple", "banana", "cherry"},
", ",
colors,
)
// Output: "{{cyan}}apple{{/}}, {{magenta}}banana{{/}}, {{yellow}}cherry{{/}}"// No color formatter
noColorFormatter := formatter.NewWithNoColorBool(true)
msg := noColorFormatter.F("{{green}}This won't be colored{{/}}")
// Terminal color formatter
terminalFormatter := formatter.New(formatter.ColorModeTerminal)
msg := terminalFormatter.F("{{green}}This will be colored{{/}}")
// Custom formatter with specific settings
customFormatter := formatter.Formatter{
ColorMode: formatter.ColorModeNone,
}import (
"github.com/onsi/ginkgo/v2/formatter"
"github.com/onsi/ginkgo/v2/types"
)
type ColoredReporter struct {
formatter formatter.Formatter
}
func NewColoredReporter(noColor bool) *ColoredReporter {
return &ColoredReporter{
formatter: formatter.NewWithNoColorBool(noColor),
}
}
func (r *ColoredReporter) DidRun(report types.SpecReport) {
if report.Failed() {
msg := r.formatter.F(
"{{red}}{{bold}}FAILED{{/}}: %s\n",
report.FullText(),
)
fmt.Fprint(formatter.ColorableStdOut, msg)
} else {
msg := r.formatter.F(
"{{green}}PASSED{{/}}: %s\n",
report.FullText(),
)
fmt.Fprint(formatter.ColorableStdOut, msg)
}
}import "github.com/onsi/ginkgo/v2/formatter"
var _ = AttachProgressReporter(func() string {
return formatter.F(
"{{cyan}}Progress:{{/}} Running spec %s",
getCurrentSpecName(),
)
})import "github.com/onsi/ginkgo/v2/formatter"
func ExpectValidUser(user *User) {
GinkgoHelper()
if user == nil {
msg := formatter.F("{{red}}Expected valid user but got nil{{/}}")
Fail(msg)
return
}
if user.Email == "" {
msg := formatter.Fi(2,
"{{red}}User validation failed:{{/}}\n"+
"{{yellow}}Email is required{{/}}",
)
Fail(msg)
}
}The core DSL provides an advanced extension point for transforming node arguments before they're processed.
Registers a transformer that can modify node arguments during tree construction.
type NodeArgsTransformer func(args []any) []any
func AddTreeConstructionNodeArgsTransformer(
transformer NodeArgsTransformer,
) func()Parameters:
transformer: Function that receives node args and returns transformed argsReturns: Function to deregister the transformer
Use Cases:
Example:
// Automatically add a label to all It nodes
var _ = AddTreeConstructionNodeArgsTransformer(func(args []any) []any {
// Check if this is an It node with a string description
if len(args) > 0 {
if _, ok := args[0].(string); ok {
// Add a label to all It nodes
args = append(args, Label("auto-labeled"))
}
}
return args
})
// Now all It nodes automatically get the label
Describe("Feature", func() {
It("test 1", func() {
// Has "auto-labeled" label
})
It("test 2", func() {
// Also has "auto-labeled" label
})
})Advanced Example - Auto-timeout:
import "time"
func init() {
// Automatically add 30s timeout to all specs
AddTreeConstructionNodeArgsTransformer(func(args []any) []any {
// Check if this looks like an It node
hasFunc := false
hasTimeout := false
for _, arg := range args {
switch arg.(type) {
case func():
hasFunc = true
case func(SpecContext):
hasFunc = true
case NodeTimeout:
hasTimeout = true
case SpecTimeout:
hasTimeout = true
}
}
// If it has a function and no timeout, add one
if hasFunc && !hasTimeout {
args = append(args, SpecTimeout(30*time.Second))
}
return args
})
}This package is deprecated. Table-driven testing functionality has moved to the core DSL.
package table
type TableSupportHasBeenMovedToTheCoreGinkgoDSL struct{}Instead of importing github.com/onsi/ginkgo/v2/extensions/table, use the table functions from the main DSL or github.com/onsi/ginkgo/v2/dsl/table.
Extensions are powerful but should be used sparingly:
Good Use Cases:
Avoid Using For:
globals.Reset() affect global state// Package testutil provides utilities for our testing framework
package testutil
import (
"github.com/onsi/ginkgo/v2/extensions/globals"
"sync"
)
var resetMutex sync.Mutex
// SafeReset provides a thread-safe wrapper around globals.Reset
func SafeReset() {
resetMutex.Lock()
defer resetMutex.Unlock()
globals.Reset()
}
// WithIsolatedSuite runs a function with an isolated Ginkgo suite
func WithIsolatedSuite(fn func()) {
SafeReset()
defer SafeReset()
fn()
}Combining multiple extensions for a custom testing framework:
package myframework
import (
. "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo/v2/extensions/globals"
"github.com/onsi/ginkgo/v2/formatter"
"github.com/onsi/ginkgo/v2/types"
)
type Framework struct {
formatter formatter.Formatter
labels []string
}
func New(labels ...string) *Framework {
return &Framework{
formatter: formatter.NewWithNoColorBool(false),
labels: labels,
}
}
func (f *Framework) Reset() {
globals.Reset()
}
func (f *Framework) DefineTest(name string, body func()) {
// Add framework labels automatically
args := []any{Label(f.labels...)}
args = append(args, body)
It(name, args...)
}
func (f *Framework) Log(msg string) {
formatted := f.formatter.F("{{cyan}}[Framework]{{/}} %s", msg)
GinkgoWriter.Println(formatted)
}
// Usage:
// fw := myframework.New("integration", "v2")
// fw.DefineTest("my test", func() {
// fw.Log("Running custom test")
// })