CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-specs2--specs2-2-10

Comprehensive testing framework and specification library for Scala that enables behavior-driven development through executable specifications

Overview
Eval results
Files

reporting.mddocs/

Reporting and HTML

Specs2 provides a flexible reporting system with multiple output formats including console, HTML, Markdown, and JUnit XML. The reporting system is designed to generate both human-readable documentation and machine-parseable results for continuous integration.

Core Reporting

Reporter

Base interface for all reporting functionality with lifecycle management.

trait Reporter {
  def prepare(env: Env, printers: List[Printer]): List[SpecificationStructure] => Action[Unit]
  def report(env: Env, printers: List[Printer]): SpecStructure => Action[Unit]
  def finalize(env: Env, printers: List[Printer]): List[SpecificationStructure] => Action[Unit]
}

The Reporter coordinates the complete reporting lifecycle:

  • Prepare: Initialize printers and set up reporting infrastructure
  • Report: Process individual specification structures
  • Finalize: Complete reporting, generate indexes, and cleanup resources

Printer

Base interface for output formatting with fold-based processing.

trait Printer {
  def prepare(env: Env, specifications: List[SpecificationStructure]): Action[Unit]
  def finalize(env: Env, specifications: List[SpecificationStructure]): Action[Unit]  
  def fold(env: Env, spec: SpecStructure): Fold[Fragment]
}

The Printer uses a fold-based approach where:

  • Prepare: Set up output files, directories, and resources
  • Fold: Process fragments incrementally using functional fold operations
  • Finalize: Complete output generation and cleanup

Fold-Based Processing

trait Fold[T] {
  def start: T
  def fold(previous: T, fragment: Fragment): T  
  def end(final: T): T
}

This enables:

  • Incremental processing: Process fragments one at a time
  • Memory efficiency: Constant memory usage regardless of specification size
  • Streaming output: Generate output as fragments are processed

Console Reporting

TextPrinter

Formats output for console display with colors and formatting.

trait TextPrinter extends Printer {
  def printText(text: String): Action[Unit] 
  def printExample(example: Example): Action[Unit]
  def printResult(result: Result): Action[Unit]
  def printStatistics(stats: Statistics): Action[Unit]
}

Console Output Example:

Calculator specification

  Calculator should
    + add two numbers correctly
    + subtract two numbers correctly  
    x handle division by zero
      '10 / 0' doesn't throw a java.lang.ArithmeticException

Total for specification Calculator:
Finished in 23 ms
3 examples, 1 failure, 0 error

Colors and Formatting

trait AnsiColors {
  def green(text: String): String
  def red(text: String): String  
  def blue(text: String): String
  def yellow(text: String): String
  def cyan(text: String): String
  def white(text: String): String
  def bold(text: String): String
}

Usage:

// Enable/disable colors
Arguments(colors = true)   // Enable colors (default)
Arguments(colors = false)  // Disable colors

// Command line
sbt "testOnly * -- colors"      # Enable colors
sbt "testOnly * -- noColors"    # Disable colors

HTML Reporting

HtmlTemplate

Generates complete HTML pages for specifications.

trait HtmlTemplate {
  def page(spec: SpecificationStructure, fragments: Fragments): NodeSeq
  def head(spec: SpecificationStructure): NodeSeq
  def body(spec: SpecificationStructure, fragments: Fragments): NodeSeq
  def css: String
  def javascript: String
}

SpecHtmlPage

Individual specification HTML page generation.

class SpecHtmlPage {
  def generate(spec: SpecificationStructure): Action[Unit]
  def generateToFile(spec: SpecurationStructure, file: File): Action[Unit]
  def toXml(spec: SpecificationStructure): NodeSeq
}

HTML Generation:

# Generate HTML reports
sbt "testOnly * -- html"

# Specify output directory  
sbt "testOnly * -- html outdir target/html-reports"

# Generate with custom CSS
sbt "testOnly * -- html css custom-styles.css"

HTML Features

Generated HTML reports include:

  • Syntax highlighting for code examples
  • Collapsible sections for large specifications
  • Navigation menu for multi-specification reports
  • Search functionality for finding specific tests
  • Statistics summaries with pass/fail counts
  • Execution timing information
  • Failure details with stack traces

Htmlx

HTML utilities and transformations.

object Htmlx {
  def render(nodes: NodeSeq): String
  def toXhtml(html: String): NodeSeq
  def format(nodes: NodeSeq): NodeSeq
  def addCss(css: String): NodeSeq => NodeSeq
  def addJavascript(js: String): NodeSeq => NodeSeq
}

Advanced HTML Features

TableOfContents

Navigation structure generation for multi-specification reports.

trait TableOfContents {
  def create(specs: List[SpecificationStructure]): NodeSeq
  def createEntry(spec: SpecificationStructure): NodeSeq
  def createSection(title: String, specs: List[SpecificationStructure]): NodeSeq
}

Indexing

Cross-reference and linking between specification pages.

trait Indexing {
  def createIndex(specs: List[SpecificationStructure]): NodeSeq
  def createLinks(spec: SpecificationStructure): Map[String, String]
  def resolveReferences(html: NodeSeq): NodeSeq
}

Multi-Specification HTML Report:

// Generate linked HTML reports for multiple specifications
val specs = List(new UserSpec, new OrderSpec, new PaymentSpec)
HtmlReporter.generateSuite(specs, "target/html-reports")

Markdown Reporting

MarkdownPrinter

Generates Markdown documentation from specifications.

trait MarkdownPrinter extends Printer {
  def printMarkdown(fragments: Fragments): Action[Unit]
  def printTitle(title: String): Action[Unit]
  def printSection(section: String): Action[Unit]
  def printCodeBlock(code: String): Action[Unit]
  def printTable(headers: List[String], rows: List[List[String]]): Action[Unit]
}

Markdown Generation:

# Generate Markdown reports
sbt "testOnly * -- markdown"

# Specify output file
sbt "testOnly * -- markdown outfile README.md"

Generated Markdown Example:

# Calculator Specification

Calculator should

- ✓ add two numbers correctly
- ✓ subtract two numbers correctly  
- ✗ handle division by zero

'10 / 0' doesn't throw a java.lang.ArithmeticException

## Statistics

- **Total**: 3 examples
- **Passed**: 2
- **Failed**: 1
- **Errors**: 0
- **Execution time**: 23ms

JUnit XML Reporting

JUnitXmlPrinter

Generates JUnit-compatible XML reports for CI integration.

trait JUnitXmlPrinter extends Printer {
  def printXml(specs: List[SpecificationStructure]): Action[Unit]
  def printTestSuite(spec: SpecificationStructure): NodeSeq
  def printTestCase(example: Example): NodeSeq
  def printFailure(failure: Result): NodeSeq
  def printError(error: Result): NodeSeq
}

JUnit XML Generation:

# Generate JUnit XML reports
sbt "testOnly * -- junitxml"

# Specify output directory
sbt "testOnly * -- junitxml outdir target/test-reports"

Generated XML Structure:

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="CalculatorSpec" tests="3" failures="1" errors="0" time="0.023">
  <testcase name="add two numbers correctly" classname="CalculatorSpec" time="0.005"/>
  <testcase name="subtract two numbers correctly" classname="CalculatorSpec" time="0.008"/>
  <testcase name="handle division by zero" classname="CalculatorSpec" time="0.010">
    <failure message="Expected exception not thrown" type="assertion">
      '10 / 0' doesn't throw a java.lang.ArithmeticException
    </failure>
  </testcase>
</testsuite>

Custom Reporting

SbtPrinter

SBT-specific output formatting for build tool integration.

trait SbtPrinter extends Printer {
  def printSbtResult(result: Result, testName: String): Action[Unit]
  def printSbtSummary(stats: Statistics): Action[Unit]
}

NotifierPrinter

Integration with test framework notifiers for IDE support.

trait NotifierPrinter extends Printer {
  def notifyStart(testName: String): Action[Unit]
  def notifyEnd(testName: String, result: Result): Action[Unit]
  def notifyError(testName: String, error: Throwable): Action[Unit]
}

Custom Printers

Create custom output formats:

trait CustomPrinter extends Printer {
  def format(fragments: Fragments): String
  def writeToFile(content: String, file: File): Action[Unit]
}

Example Custom Printer:

class JsonPrinter extends CustomPrinter {
  def print(fragments: Fragments): Action[Unit] = {
    val json = fragments.fragments.map {
      case Example(desc, result) => 
        s"""{"description":"$desc","status":"${result.status}"}"""
      case Text(text) => 
        s"""{"type":"text","content":"$text"}"""
    }.mkString("[", ",", "]")
    
    writeToFile(json, new File("target/results.json"))
  }
}

Forms and Advanced Reporting

Form

Structured data validation and reporting with tabular output.

case class Form(
  title: String = "",
  rows: List[Row] = Nil
) {
  def tr(row: Row): Form
  def th(cells: Cell*): Form  
  def td(cells: Cell*): Form
}

Field

Individual form fields with validation.

case class Field[T](
  label: String,
  value: T,
  expected: Option[T] = None
) {
  def apply(actual: T): Field[T]
  def must(matcher: Matcher[T]): Field[T]
}

Form Usage Example:

class FormReportSpec extends Specification { def is = s2"""
  User validation form
  $userValidationForm
  """
  
  def userValidationForm = Form("User Validation").
    tr(Field("Name", user.name).must(not(beEmpty))).
    tr(Field("Email", user.email).must(beMatching(emailRegex))).
    tr(Field("Age", user.age).must(beGreaterThan(0)))
}

Cards

Card-based reporting layout for structured data presentation.

trait Cards {
  def card(title: String): Card
  def toTabs: Tabs
}

case class Card(
  title: String,
  body: Fragments
) {
  def show: Fragment
}

Tabs

Tabbed interface for organizing related content.

trait Tabs {
  def tab(title: String, content: Fragments): Tab
  def show: Fragment
}

case class Tab(
  title: String,
  content: Fragments,
  active: Boolean = false
) {
  def activate: Tab
}

Statistics and Metrics

Statistics

Execution statistics collection and reporting.

case class Statistics(
  examples: Int = 0,
  successes: Int = 0, 
  failures: Int = 0,
  errors: Int = 0,
  pending: Int = 0,
  skipped: Int = 0,
  trend: Option[Statistics] = None
) {
  def total: Int = examples
  def hasFailures: Boolean = failures > 0
  def hasErrors: Boolean = errors > 0
  def isSuccess: Boolean = !hasFailures && !hasErrors
}

StatisticsRepository

Repository for persisting and retrieving execution statistics across test runs.

trait StatisticsRepository {
  def store(spec: SpecificationStructure, stats: Statistics): Action[Unit]
  def retrieve(spec: SpecificationStructure): Action[Option[Statistics]]
  def clear(spec: SpecificationStructure): Action[Unit]
}

object StatisticsRepository {
  def memory: StatisticsRepository
  def file(path: String): StatisticsRepository
}

Usage:

  • Memory repository: Statistics stored in memory for single test run
  • File repository: Statistics persisted to disk for trend analysis
  • Trend analysis: Compare current results with historical data

Timing Information

Execution timing and performance metrics.

case class ExecutionTime(
  duration: Duration,
  timestamp: Long = System.currentTimeMillis
) {
  def formatted: String
  def inMillis: Long  
  def inSeconds: Double
}

Enable Timing:

# Show execution times in console
sbt "testOnly * -- showTimes"

# Include timing in HTML reports
sbt "testOnly * -- html showTimes"

Report Configuration

Output Directory Configuration

# Set output directory for all reports
sbt "testOnly * -- outdir target/custom-reports" 

# HTML reports in specific directory
sbt "testOnly * -- html outdir target/html"

# Multiple formats with same base directory
sbt "testOnly * -- html markdown junitxml outdir target/reports"

Report Templates

Customize HTML report appearance:

// Custom CSS
Arguments(html = true, css = "custom-styles.css")

// Custom JavaScript  
Arguments(html = true, javascript = "custom-behavior.js")

// Custom template
Arguments(html = true, template = "custom-template.html")

Integration Examples

CI/CD Pipeline Integration

# GitHub Actions example
- name: Run tests with reports
  run: sbt "test -- junitxml html outdir target/test-reports"

- name: Publish test results  
  uses: dorny/test-reporter@v1
  with:
    name: Specs2 Tests
    path: target/test-reports/*.xml
    reporter: java-junit

Documentation Generation

class DocumentationSpec extends Specification { def is = 
  args(html = true, markdown = true) ^ s2"""
  
# API Documentation

This specification serves as both tests and documentation.

## User Management API

The user management system provides the following capabilities:

### User Creation
  
  create valid user                    $createValidUser
  validate required fields             $validateRequired
  handle duplicate emails              $handleDuplicates

### User Authentication

  authenticate with valid credentials  $validAuth
  reject invalid credentials          $invalidAuth
  """

Best Practices

  1. Choose appropriate formats: Console for development, HTML for documentation, JUnit XML for CI
  2. Organize output: Use consistent directory structures for different report types
  3. Customize for audience: Different styling and detail levels for different consumers
  4. Include timing: Monitor performance trends with execution timing
  5. Structure for navigation: Use sections and tables of contents for large test suites
  6. Integrate with tools: Configure reports for CI/CD and monitoring systems
  7. Document with forms: Use structured forms for complex validation scenarios
  8. Version control reports: Consider including generated documentation in source control

Install with Tessl CLI

npx tessl i tessl/maven-org-specs2--specs2-2-10

docs

core-specifications.md

dsl-components.md

index.md

integration-features.md

matcher-system.md

mutable-specifications.md

reporting.md

test-execution.md

tile.json