or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcore-dsl.mddecorators.mdextensions.mdindex.mdreporters.mdreporting.mdtable-driven-testing.mdtypes.md
tile.json

extensions.mddocs/

Extensions

Ginkgo provides extension packages for advanced use cases, including global state management and formatting utilities.

Global State Management

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

import "github.com/onsi/ginkgo/v2/extensions/globals"

Reset

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:

  • Testing frameworks that need to create multiple isolated Ginkgo test suites
  • Tools that programmatically generate and execute Ginkgo specs
  • Advanced meta-testing scenarios

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:

  • Only use Reset() if you fully understand its implications
  • After calling Reset(), all previously defined specs are lost
  • This is primarily for framework authors and testing tools
  • Normal test suites should never need this function

Formatter Package

The formatter package provides text formatting and colorization utilities used by Ginkgo's reporters.

Import

import "github.com/onsi/ginkgo/v2/formatter"

Constants

const COLS int = 80

Default column width for formatted output.

Variables

var ColorableStdOut io.Writer
var ColorableStdErr io.Writer
var SingletonFormatter Formatter
  • ColorableStdOut: Colorable stdout writer (Windows-compatible)
  • ColorableStdErr: Colorable stderr writer (Windows-compatible)
  • SingletonFormatter: Default formatter instance

Color Mode

type ColorMode uint8

const (
    ColorModeNone       ColorMode = iota
    ColorModeTerminal
    ColorModePassthrough
)
  • ColorModeNone: No color output
  • ColorModeTerminal: Color output for terminals
  • ColorModePassthrough: Pass through color codes without processing

Formatter

Text formatter with color support and layout capabilities.

type Formatter struct {
    ColorMode ColorMode
}

Constructors:

func New(colorMode ColorMode) Formatter
func NewWithNoColorBool(noColor bool) Formatter

Methods:

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,
) string

Package-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) string

Format Strings

The 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 text
  • And many more color variations...

Example:

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{{/}}"

Creating Custom Formatters

// 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,
}

Practical Examples

Custom Reporter with Formatting

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)
    }
}

Progress Reporting with Formatting

import "github.com/onsi/ginkgo/v2/formatter"

var _ = AttachProgressReporter(func() string {
    return formatter.F(
        "{{cyan}}Progress:{{/}} Running spec %s",
        getCurrentSpecName(),
    )
})

Custom Failure Messages

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)
    }
}

Advanced Node Transformation

The core DSL provides an advanced extension point for transforming node arguments before they're processed.

AddTreeConstructionNodeArgsTransformer

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 args

Returns: Function to deregister the transformer

Use Cases:

  • Automatically adding decorators to all specs
  • Injecting common setup into nodes
  • Implementing custom DSL extensions

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
    })
}

Deprecated Extensions

extensions/table

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.

Extension Best Practices

When to Use Extensions

Extensions are powerful but should be used sparingly:

Good Use Cases:

  • Building testing frameworks on top of Ginkgo
  • Creating organization-wide test utilities
  • Implementing custom reporters with rich formatting
  • Advanced tooling and code generation

Avoid Using For:

  • Normal test writing (use the standard DSL)
  • One-off test customizations (use decorators instead)
  • Replacing built-in functionality

Safety Guidelines

  1. Understand the Impact: Extensions like globals.Reset() affect global state
  2. Document Usage: Clearly document when and why extensions are used
  3. Test Thoroughly: Extension code should be well-tested
  4. Version Compatibility: Be aware that extension APIs may change between versions

Example: Safe Extension Wrapper

// 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()
}

Integration Example

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")
// })