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

reporting.mddocs/

Reporting

Ginkgo provides rich reporting capabilities including report entries, current spec information, and lifecycle reporting hooks for custom reporters.

Report Entry Management

AddReportEntry

Adds a custom report entry to the current spec. Report entries are included in all generated reports.

func AddReportEntry(name string, args ...any)

Parameters:

  • name: Entry name
  • args: Optional value and ReportEntryVisibility (or CodeLocation, Offset)

Example:

It("performs complex operation", func() {
    AddReportEntry("Starting operation")

    result := performOperation()
    AddReportEntry("Operation result", result)

    AddReportEntry("Debug info", map[string]any{
        "duration": result.Duration,
        "items": result.ItemCount,
    })

    // Only visible on failure or in verbose mode
    AddReportEntry("Detailed logs", verboseLogs,
        ReportEntryVisibilityFailureOrVerbose)

    Expect(result.Success).To(BeTrue())
})

Report Entry Visibility

Control when report entries are shown in output.

const ReportEntryVisibilityAlways
const ReportEntryVisibilityFailureOrVerbose
const ReportEntryVisibilityNever

type ReportEntryVisibility uint

Example:

It("has conditional reporting", func() {
    // Always visible
    AddReportEntry("Critical info", data, ReportEntryVisibilityAlways)

    // Visible only on failure or with -v
    AddReportEntry("Debug info", debugData,
        ReportEntryVisibilityFailureOrVerbose)

    // Never shown in console, but included in JSON/JUnit reports
    AddReportEntry("Internal metric", metric,
        ReportEntryVisibilityNever)
})

Current Spec Information

CurrentSpecReport

Returns the report for the currently running spec. Available during spec execution and in ReportAfterEach.

func CurrentSpecReport() SpecReport

Example:

It("uses spec report", func() {
    report := CurrentSpecReport()
    fmt.Printf("Running: %s\n", report.FullText())
    fmt.Printf("Location: %s\n", report.LeafNodeLocation)

    // Check labels
    if slices.Contains(report.LeafNodeLabels, "slow") {
        // Adjust behavior for slow tests
    }
})

AfterEach(func() {
    if CurrentSpecReport().Failed() {
        // Capture additional debug info on failure
        captureScreenshot()
        dumpLogs()
    }
})

CurrentTreeConstructionNodeReport

Returns report information during tree construction phase (when Describe, Context, etc. are being evaluated).

func CurrentTreeConstructionNodeReport() ConstructionNodeReport

Example:

var suiteConfig types.SuiteConfig

var _ = Describe("Suite", func() {
    // Access construction phase information
    report := CurrentTreeConstructionNodeReport()
    suiteConfig = report.SuiteConfig

    It("uses suite config", func() {
        fmt.Printf("Random seed: %d\n", suiteConfig.RandomSeed)
    })
})

Reporting Lifecycle Hooks

Reporting nodes allow you to observe and report on spec execution without affecting test outcomes.

ReportBeforeEach

Runs before each spec for reporting purposes. Does not cause spec failure if it fails.

func ReportBeforeEach(body any, args ...any) bool

Parameters:

  • body: Function accepting CurrentSpecReport() and/or SpecContext
  • args: Optional decorators

Example:

ReportBeforeEach(func(report SpecReport) {
    fmt.Printf("Starting: %s\n", report.FullText())
    startTimer(report.LeafNodeText)
})

It("does something", func() {
    // test code
})

ReportAfterEach

Runs after each spec for reporting purposes. Has access to spec results.

func ReportAfterEach(body any, args ...any) bool

Example:

ReportAfterEach(func(report SpecReport) {
    fmt.Printf("Finished: %s\n", report.FullText())
    fmt.Printf("Duration: %v\n", report.RunTime)
    fmt.Printf("State: %s\n", report.State)

    if report.Failed() {
        fmt.Printf("Failure: %s\n", report.Failure.Message)
        fmt.Printf("Location: %s\n", report.Failure.Location)
    }

    // Log report entries
    for _, entry := range report.ReportEntries {
        fmt.Printf("Entry: %s = %v\n", entry.Name, entry.Value)
    }
})

ReportBeforeSuite

Runs before suite execution starts for reporting.

func ReportBeforeSuite(body any, args ...any) bool

Example:

ReportBeforeSuite(func(report Report) {
    fmt.Printf("Suite: %s\n", report.SuiteDescription)
    fmt.Printf("Path: %s\n", report.SuitePath)
    fmt.Printf("Total specs: %d\n", report.PreRunStats.TotalSpecs)
    fmt.Printf("Will run: %d\n", report.PreRunStats.SpecsThatWillRun)
})

ReportAfterSuite

Runs after suite completes for reporting. Has access to full suite results.

func ReportAfterSuite(text string, body any, args ...any) bool

Parameters:

  • text: Description for the report hook
  • body: Function accepting Report
  • args: Optional decorators

Example:

ReportAfterSuite("suite report", func(report Report) {
    fmt.Printf("Suite: %s\n", report.SuiteDescription)
    fmt.Printf("Success: %v\n", report.SuiteSucceeded)
    fmt.Printf("Duration: %v\n", report.RunTime)

    // Count results
    passed := report.SpecReports.CountWithState(types.SpecStatePassed)
    failed := report.SpecReports.CountOfFailed()
    skipped := report.SpecReports.CountOfSkipped()
    pending := report.SpecReports.CountOfPending()

    fmt.Printf("Passed: %d, Failed: %d, Skipped: %d, Pending: %d\n",
        passed, failed, skipped, pending)

    // Report flaky specs
    for _, spec := range report.SpecReports {
        if spec.NumAttempts > 1 {
            fmt.Printf("Flaky: %s (took %d attempts)\n",
                spec.FullText(), spec.NumAttempts)
        }
    }
})

Report Types

SpecReport

Complete report for a single spec execution.

type SpecReport struct {
    // Hierarchy and identification
    ContainerHierarchyTexts    []string
    ContainerHierarchyLocations []CodeLocation
    ContainerHierarchyLabels   [][]string
    LeafNodeType               NodeType
    LeafNodeLocation           CodeLocation
    LeafNodeLabels             []string
    LeafNodeText               string

    // Execution information
    State           SpecState
    StartTime       time.Time
    EndTime         time.Time
    RunTime         time.Duration
    ParallelProcess int

    // Failure information
    Failure Failure

    // Retry information
    NumAttempts          int
    MaxFlakeAttempts     int
    MaxMustPassRepeatedly int

    // Output and entries
    CapturedGinkgoWriterOutput string
    CapturedStdOutErr          string
    ReportEntries              ReportEntries
    SpecEvents                 SpecEvents
}

Methods:

func (report SpecReport) FullText() string
func (report SpecReport) Failed() bool
func (report SpecReport) IsSerial() bool
func (report SpecReport) IsInOrderedContainer() bool
func (report SpecReport) CombinedOutput() string

Example:

ReportAfterEach(func(report SpecReport) {
    if report.Failed() {
        fmt.Printf("Failed: %s\n", report.FullText())
        fmt.Printf("At: %s\n", report.Failure.Location)
        fmt.Printf("Message: %s\n", report.Failure.Message)

        // Check if it's in an ordered container
        if report.IsInOrderedContainer() {
            fmt.Println("Part of ordered specs")
        }

        // Print all output
        fmt.Println("Output:")
        fmt.Println(report.CombinedOutput())
    }
})

ConstructionNodeReport

Report available during tree construction.

type ConstructionNodeReport struct {
    CodeLocation                   CodeLocation
    ContainerHierarchyTexts        []string
    ContainerHierarchyLocations    []CodeLocation
    ContainerHierarchyLabels       [][]string
    SuiteConfig                    SuiteConfig
}

Methods:

func (report ConstructionNodeReport) FullText() string
func (report ConstructionNodeReport) Labels() []string

Report

Complete suite execution report.

type Report struct {
    // Suite identification
    SuiteDescription string
    SuitePath        string
    SuiteSucceeded   bool

    // Timing
    StartTime time.Time
    EndTime   time.Time
    RunTime   time.Duration

    // Configuration and stats
    SuiteConfig SuiteConfig
    PreRunStats PreRunStats
    SpecReports SpecReports

    // Special conditions
    SuiteHasProgrammaticFocus  bool
    SpecialSuiteFailureReasons []string
}

Methods:

func (report Report) Add(other Report) Report

ReportEntry

Individual report entry attached to a spec.

type ReportEntry struct {
    Visibility       ReportEntryVisibility
    Location         CodeLocation
    Time             time.Time
    TimelineLocation TimelineLocation
    Name             string
    Value            any
}

Methods:

func (entry ReportEntry) GetRawValue() any
func (entry ReportEntry) StringRepresentation() string

ReportEntries

Collection of report entries with query methods.

type ReportEntries []ReportEntry

Methods:

func (entries ReportEntries) HasVisibility(
    visibilities ...ReportEntryVisibility,
) ReportEntries
func (entries ReportEntries) WithName(name string) ReportEntry

Example:

ReportAfterEach(func(report SpecReport) {
    // Get all entries visible on failure
    failureEntries := report.ReportEntries.HasVisibility(
        types.ReportEntryVisibilityFailureOrVerbose,
    )

    for _, entry := range failureEntries {
        fmt.Printf("%s: %v\n", entry.Name, entry.Value)
    }

    // Find specific entry
    debugEntry := report.ReportEntries.WithName("debug-info")
    if debugEntry.Name != "" {
        fmt.Printf("Debug: %v\n", debugEntry.Value)
    }
})

Custom Reporting Patterns

Capturing Timing Information

var timings = map[string]time.Duration{}

ReportBeforeEach(func(report SpecReport) {
    timings[report.FullText()] = time.Now()
})

ReportAfterEach(func(report SpecReport) {
    start := timings[report.FullText()]
    duration := time.Since(start)
    AddReportEntry("Precise duration", duration)
})

Conditional Debug Output

AfterEach(func() {
    report := CurrentSpecReport()
    if report.Failed() {
        // Add debug information only on failure
        AddReportEntry("System State", captureSystemState())
        AddReportEntry("Recent Logs", getRecentLogs())
        AddReportEntry("Memory Usage", getMemoryStats())
    }
})

Progress Tracking

var totalSpecs int
var completedSpecs int

ReportBeforeSuite(func(report Report) {
    totalSpecs = report.PreRunStats.SpecsThatWillRun
    completedSpecs = 0
})

ReportAfterEach(func(report SpecReport) {
    completedSpecs++
    progress := float64(completedSpecs) / float64(totalSpecs) * 100
    fmt.Printf("Progress: %.1f%% (%d/%d)\n",
        progress, completedSpecs, totalSpecs)
})

Performance Monitoring

var slowSpecs []SpecReport

ReportAfterEach(func(report SpecReport) {
    threshold := 5 * time.Second
    if report.RunTime > threshold {
        slowSpecs = append(slowSpecs, report)
        AddReportEntry("Slow spec warning",
            fmt.Sprintf("Took %v", report.RunTime))
    }
})

ReportAfterSuite("slow specs report", func(report Report) {
    if len(slowSpecs) > 0 {
        fmt.Println("Slow specs:")
        for _, spec := range slowSpecs {
            fmt.Printf("  %s: %v\n", spec.FullText(), spec.RunTime)
        }
    }
})

Integration with External Systems

ReportAfterEach(func(report SpecReport) {
    // Send metrics to monitoring system
    metrics.RecordSpecDuration(
        report.FullText(),
        report.RunTime.Seconds(),
    )

    if report.Failed() {
        metrics.IncrementFailureCount(report.FullText())

        // Send alert for critical tests
        if slices.Contains(report.LeafNodeLabels, "critical") {
            alerting.SendAlert("Critical test failed: " + report.FullText())
        }
    }
})

Generating Custom Reports

ReportAfterSuite("CSV report generator", func(report Report) {
    // Generate custom CSV report
    file, _ := os.Create("test-results.csv")
    defer file.Close()

    writer := csv.NewWriter(file)
    writer.Write([]string{"Spec", "State", "Duration", "Attempts"})

    for _, spec := range report.SpecReports {
        writer.Write([]string{
            spec.FullText(),
            spec.State.String(),
            spec.RunTime.String(),
            fmt.Sprintf("%d", spec.NumAttempts),
        })
    }
    writer.Flush()
})