minitest

package module
v0.0.0-...-42021a9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

go-ruby-minitest/minitest

minitest — go-ruby-minitest

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the core of Ruby's Minitest test framework (targeting Minitest 5.x) — the assertion layer, the per-test run lifecycle, result aggregation, the spec DSL → assertion mapping, and the Mock / Stub object framework. It is the deterministic, interpreter-independent heart of Minitest: given a Ruby runtime to supply value semantics, it produces the exact same assertion failure messages, mu_pp inspection output, and run-result counts as the minitest gem, byte-for-byte.

It is the Minitest backend for go-embedded-ruby (rbgo) — binding it lets real Minitest test suites run on a pure-Go Ruby — but is a standalone, reusable module with no dependency on the Ruby runtime, a sibling of go-ruby-regexp, go-ruby-erb, and go-ruby-yaml.

What it is — and isn't. The load-bearing part of Minitest is the wording: the "Expected: 1\n Actual: 2" of assert_equal, the mu_pp inspection, the custom-message prepend, the mock-verify diagnostics. That formatting, the run orchestration (before_setup/setup/… → body → teardown), result coding, and mock bookkeeping are fully deterministic and live here as pure Go. Evaluating the test bodies and the value-level operations an assertion compares with (#==, #=~, #inspect, #include?, #respond_to?, …) are the host's job, funnelled through one explicit Runtime seam.

Features

A faithful port of Minitest 5.x's core, validated against the minitest gem on every supported platform:

  • Assertionsassert/refute, assert_equal/refute_equal, assert_nil, assert_empty, assert_includes, assert_match/refute_match, assert_instance_of/assert_kind_of, assert_respond_to, assert_raises, assert_throws, assert_in_delta/assert_in_epsilon, assert_operator, assert_predicate, assert_same/refute_same, assert_output, assert_silent, flunk, skip, pass — each with its byte-exact failure message, mu_pp / mu_pp_for_diff, the message/msg prepend, and the assertion counter.
  • Run lifecycleTest#run driving before_setup/setup/after_setup → the test body → before_teardown/teardown/after_teardown, capturing exceptions into failures (one capture_exceptions for setup+body, one per teardown hook), and producing a Result.
  • Result — assertion count, failures / errors / skips, result_code, result_label, location, and the to_s rendering, with the Assertion / Skip / UnexpectedError exception model.
  • Spec DSLdescribe/it/before/after, the it-name morphing (test_%04d_%s), let name validation, spec_type selection, and the full must_*/wont_* → assertion mapping table with each method's flip rule.
  • Mock (expect / verify) and Stub (stub a method for a block), including the exact MockExpectationError / ArgumentError / NoMethodError messages and the under-called-vs-never-called verify distinction.

CGO-free, dependency-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three OSes (Linux, macOS, Windows).

Install

go get github.com/go-ruby-minitest/minitest

Usage

A host implements the Runtime seam over its own Ruby value model; the library then produces gem-identical messages and run results.

package main

import (
	"fmt"

	"github.com/go-ruby-minitest/minitest"
)

func main() {
	a := minitest.NewAssertions(myRuntime{}) // myRuntime implements Runtime

	// A failing assert_equal yields the gem's exact message.
	err, _ := a.AssertEqual(rInt(1), rInt(2), "")
	fmt.Println(err)
	// Expected: 1
	//   Actual: 2

	// Drive a test's lifecycle and aggregate the result.
	res, _ := minitest.RunTest(myBody{}, elapsed)
	fmt.Println(res.ResultCode(), res.Passed(), res.Assertions)
}

The seams (what the host wires)

Everything requiring genuine Ruby semantics is a seam; the library owns only the pure formatting / orchestration / aggregation:

Seam Interface What the host supplies
Value semantics Runtime #inspect, #==, #equal?/object_id, #=~, #respond_to?, #include?, #empty?, #nil?, #instance_of?/#kind_of?, class name, truthiness, #__send__
Test body & hooks TestBody invoke a named setup/teardown hook or test_* body in the VM; report the captured exception
assert_raises / assert_throws / assert_output RaiseOutcome / ThrowOutcome / per-stream results run the block, classify what it raised / threw / printed
Mock matching MockMatcher Ruby case-equality (===/==), #inspect, and expect-block invocation
Stub StubHarness alias/define/undef the singleton method; run the user block

API

// Assertions — every assert_*/refute_* returns nil on pass or a *Assertion/*Skip
// describing the failure; the host raises it and the lifecycle captures it.
func NewAssertions(rt Runtime) *Assertions
func (a *Assertions) AssertEqual(exp, act Value, msg string) (err error, deprecated bool)
func (a *Assertions) AssertNil/AssertEmpty/AssertIncludes/AssertMatch/…(…) error
func (a *Assertions) MuPP(obj Value) string          // mu_pp
func (a *Assertions) MuPPForDiff(obj Value) string   // mu_pp_for_diff

// Lifecycle — RunTest reproduces Test#run and returns a *Result (Result.from).
func RunTest(body TestBody, elapsed float64) (res *Result, abort *Passthrough)
type Result struct { /* Klass, TestName, Assertions, Failures, Time, … */ }
func (r *Result) Passed/Skipped/Errored() bool
func (r *Result) ResultCode() string                 // ".", "F", "E", "S"
func (r *Result) Location(baseDir string) string
func (r *Result) String(baseDir string) string       // Result#to_s

// Spec DSL → assertion mapping.
func ItName(seq int, desc string) string             // "test_%04d_%s"
func ValidateLetName(name string, reserved []string) string
func LookupExpectation(method string) (Expectation, bool) // must_*/wont_* table

// Mock / Stub.
func NewMock(m MockMatcher) *Mock
func (m *Mock) Expect(name string, retval Value, args []Value, kwargs []KV, block bool) error
func (m *Mock) Call(name string, args []Value, kwargs []KV) (Value, error)
func (m *Mock) Verify() error
func Stub(h StubHarness) error

Tests & coverage

The suite pairs deterministic, ruby-free tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential gem oracle: every assertion's failure message, mu_pp output, run-result tuple, and mock-verify diagnostic is computed here and compared byte-for-byte against the live minitest 5.x gem. The oracle scripts $stdout.binmode so Windows text-mode never pollutes the bytes, and skip themselves where ruby or the 5.x gem is absent. (A developer whose system ruby is too new for the 5.x gem can point the oracle at an unpacked 5.x tree via MINITEST5_LIB=…/lib.)

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-minitest/minitest authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package minitest is a pure-Go (CGO-free) re-implementation of the CORE of Ruby's Minitest test framework (targeting Minitest 5.x): the assertion layer, the per-test run lifecycle, result aggregation, the spec DSL → assertion mapping, and the Mock / Stub object framework.

It is the deterministic, interpreter-independent heart of Minitest — the part that, given a Ruby runtime to supply value semantics, produces the exact same assertion failure messages, mu_pp inspection output, and run-result counts as the minitest gem, byte-for-byte. A host such as go-embedded-ruby (rbgo) binds it so that real Minitest test suites run on a pure-Go Ruby.

What this package owns

  • Assertions: every assert_*/refute_* method, flunk, skip, pass — and, the load-bearing part, the EXACT failure message each produces (the "Expected: 1\n Actual: 2" of assert_equal, the mu_pp inspection, the custom-message prepend via [Assertions.Message]).
  • The run lifecycle: [Test.Run] drives before_setup/setup/after_setup → the test body → before_teardown/teardown/after_teardown, captures exceptions into failures, and produces a Result.
  • Result: assertion count, failures / errors / skips, result_code, result_label, location, and the to_s rendering.
  • The exception model: Assertion, Skip, UnexpectedError.
  • The spec DSL surface: describe/it/before/after and the must_*/wont_* expectation → assertion mapping (Expectations).
  • Mock (expect / verify) and Stub (stub a method for a block), including the exact MockExpectationError / ArgumentError messages.

What is a seam (supplied by the host)

Everything that requires genuine Ruby object semantics is funneled through the Runtime interface, which the host (rbgo) implements over its own object graph:

  • value inspection (#inspect), equality (#==), identity (#equal? / object_id), regexp match (#=~), #respond_to?, #include?, #empty?, #nil?, #instance_of?, #kind_of?, class name, truthiness, and arbitrary #__send__ for operator / predicate assertions.

The IO captured by assert_output / assert_silent and the execution of test method BODIES and assertion blocks are also seams — the host wires those to the Ruby VM. This package never evaluates Ruby; it only formats, orchestrates, and aggregates.

Index

Constants

View Source
const DefaultItDesc = "anonymous"

DefaultItDesc is the description it uses when none is given ("anonymous"); the host also substitutes the skip("(no tests defined)") body when no block is passed.

View Source
const E = "\x00E\x00"

E is the empty-ending sentinel assert_equal passes so its diff message gets no trailing ".", matching `message(msg, E) { diff exp, act }`.

View Source
const VERSION = "5.25.5"

VERSION is the Minitest version this package targets and reports. The failure messages, mu_pp output, and run-result semantics reproduced here are the 5.x ones (assert_equal nil deprecation-warns rather than failing, etc.).

Variables

View Source
var SetupMethods = []string{"before_setup", "setup", "after_setup"}

SetupMethods is Minitest::Test::SETUP_METHODS — the hooks run, in order, before the test body, all inside one capture_exceptions.

View Source
var TeardownMethods = []string{"before_teardown", "teardown", "after_teardown"}

TeardownMethods is Minitest::Test::TEARDOWN_METHODS — the hooks run, in order, after the test body, EACH inside its own capture_exceptions.

View Source
var UNDEFINED = &undefined{}

UNDEFINED is Minitest::Assertions::UNDEFINED, the sentinel distinguishing assert_operator's 2-arg (predicate) and 3-arg (operator) forms. Its inspect is "UNDEFINED".

Functions

func ItName

func ItName(seq int, desc string) string

ItName reproduces Minitest::Spec::DSL#it's method-name morphing:

name = "test_%04d_%s" % [@specs, desc]

where seq is the 1-based count of it/specify blocks defined so far in the describe class. The host maintains the counter and the define_method; this function owns the exact name format the reporter and filters key off of.

func RunTest

func RunTest(body TestBody, elapsed float64) (res *Result, abort *Passthrough)

RunTest reproduces Minitest::Test#run: it runs the setup hooks and the body in one capture, then each teardown hook in its own capture, accumulating failures, and returns the *Result (Result.from self). If a passthrough exception occurs, abort is the wrapping *Passthrough and the partial result is still returned (Ruby would unwind, but the host decides what to do with abort).

elapsed is the measured wall time for the whole run (time_it); the host clocks it around the call, or passes a deterministic value in tests.

func SpecType

func SpecType(matches []bool) int

SpecType reproduces Minitest::Spec::DSL#spec_type's selection: it walks the registered (matcher, class) pairs newest-first and returns the index of the first whose matcher matches desc. A matcher is either a callable (the host evaluates it and reports the boolean via callMatch) or a Regexp/=== matcher (the host reports the boolean via the same callback). The base registration (//, Minitest::Spec) always matches, so a valid index is always returned.

matches[i] is the precomputed boolean of registration i against desc, in the SAME order DSL::TYPES holds them (newest unshifted to the front). SpecType returns the first true index.

func Stub

func Stub(h StubHarness) (err error)

Stub reproduces Object#stub's control flow: Install the stub, run the block, and ALWAYS Restore afterward (the Ruby `ensure`), even if Install's precondition fails or the block raises. The block's error (or Install's) is returned; Restore's effect is unconditional.

func ValidateLetName

func ValidateLetName(name string, reservedMethods []string) string

ValidateLetName reproduces Minitest::Spec::DSL#let's name guard. It returns a non-empty error message (the exact ArgumentError text) when name is illegal: it may not begin with "test", and may not override a Minitest::Spec instance method (other than "subject"). reservedMethods is the host-supplied list of Minitest::Spec instance-method names (already excluding "subject"), so the override check stays interpreter-faithful without this package hardcoding the method set.

Types

type Assertion

type Assertion struct {
	Msg string
	// Backtrace is the (already filtered or raw) Ruby backtrace, most-recent
	// frame first, as the host captured it. Location uses it to point at the
	// offending line.
	Backtrace []string
}

Assertion is Minitest::Assertion — the failure raised by a failing assertion. In Ruby it subclasses Exception (not StandardError) so test bodies don't accidentally rescue it. Here it is a Go error carrying the failure message and the backtrace the host supplies for the raising frame.

func (*Assertion) Error

func (a *Assertion) Error() string

Error implements error; it returns the assertion message.

func (*Assertion) Location

func (a *Assertion) Location() string

Location returns the "file:line" the assertion should be attributed to: the frame just past the deepest assertion-helper frame (Minitest::Assertion#location). bt is the already-backtrace-filtered list (host responsibility), most-recent frame first as Ruby presents it. The ":in ..." suffix is stripped.

func (*Assertion) Message

func (a *Assertion) Message() string

Message returns the assertion message (Ruby Exception#message).

func (*Assertion) ResultCode

func (a *Assertion) ResultCode() string

ResultCode is the single-character code for a plain Assertion ("F").

func (*Assertion) ResultLabel

func (a *Assertion) ResultLabel() string

ResultLabel is the long label for this kind of result: "Failure".

type Assertions

type Assertions struct {

	// Count mirrors the Ruby `assertions` accessor: every assert call bumps it.
	Count int
	// contains filtered or unexported fields
}

Assertions is Minitest::Assertions bound to a Runtime. It owns the assertion counter and produces the exact failure messages of the minitest gem. Each assert_*/refute_* method returns nil on success or a non-nil error (an *Assertion or *Skip) describing the failure; the host raises it in the Ruby VM and the lifecycle ([Test]) captures it.

The value-level comparisons (==, =~, inspect, include?, …) are delegated to the Runtime; this type owns only the formatting and the assertion bookkeeping.

func NewAssertions

func NewAssertions(rt Runtime) *Assertions

NewAssertions returns an Assertions backed by rt.

func (*Assertions) Assert

func (a *Assertions) Assert(test Value, msg string) error

Assert is Minitest::Assertions#assert: increments the count and fails with msg (default "Expected #{mu_pp test} to be truthy.") unless test is truthy.

func (*Assertions) AssertEmpty

func (a *Assertions) AssertEmpty(obj Value, msg string) error

AssertEmpty is assert_empty. It first asserts obj responds to :empty? (so a non-respond_to value fails with the respond_to message), then asserts empty.

func (*Assertions) AssertEqual

func (a *Assertions) AssertEqual(exp, act Value, msg string) (err error, deprecated bool)

AssertEqual is assert_equal. On failure the message is the diff form ("Expected: …\n Actual: …"). It returns, additionally, the 5.x nil-deprecation signal: when exp is nil and the host wants the warning, deprecated is true (the gem warns rather than failing in 5.x). The host emits the warning text; the assertion result itself is unaffected.

func (*Assertions) AssertInDelta

func (a *Assertions) AssertInDelta(exp, act, delta float64, msg string) error

AssertInDelta is assert_in_delta. n = |exp - act|; fails unless delta >= n. The message interpolates exp/act/n/delta via Ruby #to_s, which the host renders by formatting the float values; here exp/act/delta/n are Go float64.

func (*Assertions) AssertInEpsilon

func (a *Assertions) AssertInEpsilon(exp, act, epsilon float64, msg string) error

AssertInEpsilon is assert_in_epsilon: delegates to assert_in_delta with delta = min(|exp|, |act|) * epsilon.

func (*Assertions) AssertIncludes

func (a *Assertions) AssertIncludes(collection, obj Value, msg string) error

AssertIncludes is assert_includes.

func (*Assertions) AssertInstanceOf

func (a *Assertions) AssertInstanceOf(cls, obj Value, msg string) error

AssertInstanceOf is assert_instance_of.

func (*Assertions) AssertKindOf

func (a *Assertions) AssertKindOf(cls, obj Value, msg string) error

AssertKindOf is assert_kind_of.

func (*Assertions) AssertMatch

func (a *Assertions) AssertMatch(matcher, obj Value, msg string) error

AssertMatch is assert_match. A String matcher is promoted to a Regexp first (so the message shows the original matcher's inspect). It asserts matcher responds to :=~, then asserts the match.

func (*Assertions) AssertNil

func (a *Assertions) AssertNil(obj Value, msg string) error

AssertNil is assert_nil.

func (*Assertions) AssertOperator

func (a *Assertions) AssertOperator(o1 Value, op string, o2 Value, msg string) error

AssertOperator is assert_operator: fails unless o1.__send__(op, o2). When o2 is the UNDEFINED sentinel it delegates to assert_predicate.

func (*Assertions) AssertOutput

func (a *Assertions) AssertOutput(stderrResult, stdoutResult error) error

AssertOutput is assert_output. The host captures stdout/stderr from the block and reports them. stdoutExp/stderrExp are the expectations: nil means "don't care"; a string means exact match; a regexp means pattern. The host signals which mode applies per stream and supplies, for an exact-string mismatch, the diff message, or for a regexp mismatch, the assert_match message.

To keep the assertion logic pure, the comparison itself is done by the host (it owns == and =~ over Ruby strings/regexps); AssertOutput is handed the already-decided pass/fail and, on failure, the composed sub-message. It returns nil on full success or the first failing stream's *Assertion.

outErr/outStd are per-stream results: nil = stream not checked or passed.

func (*Assertions) AssertPredicate

func (a *Assertions) AssertPredicate(o1 Value, op, msg string) error

AssertPredicate is assert_predicate: fails unless o1.__send__(op).

func (*Assertions) AssertRaises

func (a *Assertions) AssertRaises(out RaiseOutcome, customMsg, expArrayInspect, expSingleInspect string) (reRaise bool, err error)

AssertRaises is assert_raises. exp are the expected exception-class Values (their inspect is used in messages); customMsg is an optional leading message the gem renders as "#{msg}.\n" when present (it was the trailing String arg).

It returns (reRaise, err): when reRaise is non-nil the host must re-raise that exact outcome's exception (the Assertion/Skip/Signal/SystemExit passthrough cases). Otherwise err is nil on success (the matched exception is "returned" by the host from its own block driver) or an *Assertion describing the failure.

expInspect is mu_pp(exp) when exp has >1 element, else mu_pp(exp.first) — the host passes the already-inspected forms via expSingleInspect (for the size==1 "expected but nothing raised" / unexpected-error case) and expArrayInspect.

func (*Assertions) AssertRespondTo

func (a *Assertions) AssertRespondTo(obj Value, meth, msg string, includeAll bool) error

AssertRespondTo is assert_respond_to.

func (*Assertions) AssertSame

func (a *Assertions) AssertSame(exp, act Value, msg string) error

AssertSame is assert_same: fails unless exp.equal?(act).

func (*Assertions) AssertThrows

func (a *Assertions) AssertThrows(out ThrowOutcome, symInspect string, msg string) error

AssertThrows is assert_throws. symInspect is mu_pp(sym) (e.g. ":foo"). When the host caught a *different* symbol it sets OtherSym so the message gains the ", not …" tail, matching the gem's ArgumentError/NameError handling.

func (*Assertions) Flunk

func (a *Assertions) Flunk(msg string) error

Flunk is Minitest::Assertions#flunk: always fails with msg (default "Epic Fail!").

func (*Assertions) MuPP

func (a *Assertions) MuPP(obj Value) string

MuPP is Minitest::Assertions#mu_pp: a human-readable rendering of obj. It is obj.inspect, re-encoded to the default external encoding; for a String whose encoding differs from the default external (or is invalid) it is prefixed with "# encoding:" / "# valid:" annotation lines, exactly as the gem does.

The encoding re-encode itself is a no-op at the string level here (the host's Inspect already yields the bytes); the annotation branch is what the gem's callers observe, and it is reproduced faithfully.

func (*Assertions) MuPPForDiff

func (a *Assertions) MuPPForDiff(obj Value) string

MuPPForDiff is Minitest::Assertions#mu_pp_for_diff: a more diff-able rendering. It runs Assertions.MuPP first, then, when the inspected string contains EITHER single-escaped newlines ("\n") OR double-escaped ("\\n") but not both, unescapes them, and finally anonymizes hex object ids. This is used when building a structural diff; the package exposes it because hosts and tests assert on it.

func (*Assertions) OutputRequiresBlock

func (a *Assertions) OutputRequiresBlock() error

OutputRequiresBlock returns the flunk the gem raises when assert_output is called without a block.

func (*Assertions) Pass

func (a *Assertions) Pass() error

Pass is Minitest::Assertions#pass: counts an assertion and always succeeds.

func (*Assertions) RaisesRequiresBlock

func (a *Assertions) RaisesRequiresBlock() error

RaisesRequiresBlock returns the flunk assert_raises raises without a block.

func (*Assertions) Refute

func (a *Assertions) Refute(test Value, msg string) error

Refute is Minitest::Assertions#refute: fails if test is truthy.

func (*Assertions) RefuteEmpty

func (a *Assertions) RefuteEmpty(obj Value, msg string) error

RefuteEmpty is refute_empty.

func (*Assertions) RefuteEqual

func (a *Assertions) RefuteEqual(exp, act Value, msg string) error

RefuteEqual is refute_equal.

func (*Assertions) RefuteInDelta

func (a *Assertions) RefuteInDelta(exp, act, delta float64, msg string) error

RefuteInDelta is refute_in_delta.

func (*Assertions) RefuteInEpsilon

func (a *Assertions) RefuteInEpsilon(exp, act, epsilon float64, msg string) error

RefuteInEpsilon is refute_in_epsilon: delegates to refute_in_delta with delta = exp * epsilon (note: the gem uses a*epsilon, not min, for refute).

func (*Assertions) RefuteIncludes

func (a *Assertions) RefuteIncludes(collection, obj Value, msg string) error

RefuteIncludes is refute_includes.

func (*Assertions) RefuteInstanceOf

func (a *Assertions) RefuteInstanceOf(cls, obj Value, msg string) error

RefuteInstanceOf is refute_instance_of.

func (*Assertions) RefuteKindOf

func (a *Assertions) RefuteKindOf(cls, obj Value, msg string) error

RefuteKindOf is refute_kind_of.

func (*Assertions) RefuteMatch

func (a *Assertions) RefuteMatch(matcher, obj Value, msg string) error

RefuteMatch is refute_match.

func (*Assertions) RefuteNil

func (a *Assertions) RefuteNil(obj Value, msg string) error

RefuteNil is refute_nil.

func (*Assertions) RefuteOperator

func (a *Assertions) RefuteOperator(o1 Value, op string, o2 Value, msg string) error

RefuteOperator is refute_operator.

func (*Assertions) RefutePredicate

func (a *Assertions) RefutePredicate(o1 Value, op, msg string) error

RefutePredicate is refute_predicate.

func (*Assertions) RefuteRespondTo

func (a *Assertions) RefuteRespondTo(obj Value, meth, msg string, includeAll bool) error

RefuteRespondTo is refute_respond_to.

func (*Assertions) RefuteSame

func (a *Assertions) RefuteSame(exp, act Value, msg string) error

RefuteSame is refute_same.

func (*Assertions) SkipError

func (a *Assertions) SkipError(msg string) *Skip

SkipError builds the *Skip that Minitest::Assertions#skip raises (it does not bump the assertion count). Default message "Skipped, no message given".

type Expectation

type Expectation struct {
	// Method is the must_/wont_ name (e.g. "must_equal").
	Method string
	// Assertion is the underlying assert_/refute_ name (e.g. "assert_equal").
	Assertion string
	// Flip is how target/args bind to the assertion's parameters.
	Flip Flip
}

Expectation maps a must_*/wont_* expectation to its underlying assertion. Mapped is built from minitest/expectations.rb's infect_an_assertion table.

func Expectations

func Expectations() []Expectation

Expectations lists every supported must_*/wont_* expectation in declaration order. Hosts iterate it to install the DSL methods.

func LookupExpectation

func LookupExpectation(method string) (Expectation, bool)

LookupExpectation returns the Expectation mapping for a must_/wont_ method name and whether it is known.

func (Expectation) BindArgs

func (e Expectation) BindArgs(target Value, args []Value) (out []Value, blockIsTarget bool)

BindArgs computes the positional arguments to pass to the underlying assertion for an expectation call, given the expectation target and the expectation's own args, per the Flip rule. For FlipBlock the target is the block (not a positional arg), so the returned args are the expectation args unchanged and blockIsTarget is true; the host passes target as the &block. For the other flips the returned slice is the full positional argument list.

type Flip

type Flip int

Flip describes how an expectation's receiver and arguments map onto the underlying assertion's positional parameters (the third arg to infect_an_assertion). The zero value FlipDefault is the common case.

const (
	// FlipDefault: ctx.meth(args.first, target, *args[1..-1]) — the expectation
	// target becomes the assertion's SECOND positional arg (the "actual"), and
	// the first expectation arg becomes the first (the "expected"). Used by
	// must_equal, must_be_instance_of, etc.
	FlipDefault Flip = iota
	// FlipReverse (:reverse / dont_flip): ctx.meth(target, *args) — target stays
	// first. Used by must_include, must_be, must_respond_to, etc.
	FlipReverse
	// FlipUnary (:unary): like reverse, target first, no extra args. Used by
	// must_be_nil, must_be_empty, etc. (modeled identically to reverse here, as
	// the gem's :unary is also dont_flip=true).
	FlipUnary
	// FlipBlock (:block): ctx.meth(*args, &target) when target is a Proc — the
	// expectation target is the BLOCK. Used by must_raise, must_output, etc.
	FlipBlock
)

type KV

type KV struct {
	Key string
	Val Value
}

KV is a keyword key/value pair preserving declaration order.

type Mock

type Mock struct {
	// contains filtered or unexported fields
}

Mock is Minitest::Mock: a clean mock object. expect queues expectations; the host routes method calls into Mock.Call; verify checks all queued expectations were met. Argument case-equality and block invocation are delegated to a MockMatcher.

func NewMock

func NewMock(matcher MockMatcher) *Mock

NewMock returns a Mock backed by matcher.

func (*Mock) Call

func (m *Mock) Call(name string, args []Value, kwargs []KV) (Value, error)

Call routes a method invocation into the mock, reproducing Mock#method_missing. It returns the expectation's retval on success, or an error: *MockNoMethodError for an unmocked name, *MockExpectationError for no-more-expects / wrong args / failed block / wrong keywords, or *MockArgumentError for an arity mismatch.

func (*Mock) Expect

func (m *Mock) Expect(name string, retval Value, args []Value, kwargs []KV, block bool) error

Expect queues an expectation for name with return value retval and expected positional args / keyword matchers. It mirrors Mock#expect's argument validation, returning the ArgumentError the gem raises when args is misused. Pass block=true (with empty args/kwargs) for the block-validated form.

func (*Mock) ExpectAnyKwargs

func (m *Mock) ExpectAnyKwargs(name string, retval Value, args []Value)

ExpectAnyKwargs queues an expectation whose keyword matching accepts any keywords (the gem's `Hash == expected_kwargs` branch, set when expect is given the Hash class itself as the kwargs matcher).

func (*Mock) Verify

func (m *Mock) Verify() error

Verify reproduces Mock#verify. For each queued method: if it was never called at all (actual defaults to nil), it raises "Expected <expected[0]>"; if it was called but fewer times than expected, it raises "Expected <expected[actual.size]>, got [<actual calls>]". Else returns nil.

type MockArgumentError

type MockArgumentError struct{ Msg string }

MockArgumentError is the Ruby ArgumentError Mock raises on an arity mismatch. It is distinct from MockExpectationError because the gem raises ArgumentError (not MockExpectationError) for wrong argument/keyword counts.

func (*MockArgumentError) Error

func (e *MockArgumentError) Error() string

Error implements error.

type MockExpectationError

type MockExpectationError struct{ Msg string }

MockExpectationError is Minitest's MockExpectationError, raised by Mock when it is not called as expected (verify, no-more-expects, argument mismatch).

func (*MockExpectationError) Error

func (e *MockExpectationError) Error() string

Error implements error.

type MockMatcher

type MockMatcher interface {
	// Match reports whether expected matches actual under Ruby case-equality.
	Match(expected, actual Value) bool
	// Inspect renders v as Ruby #inspect would (for error messages).
	Inspect(v Value) string
	// CallBlock invokes the block given to expect with the actual args/kwargs and
	// reports its truthiness (the gem's val_block.call(*args, **kwargs, &block)).
	CallBlock(idx int, args []Value, kwargs []KV) bool
}

MockMatcher is the seam for Ruby case-equality used during mock argument matching: Match reports (expected === actual) || (expected == actual), and Inspect renders a Value for the error messages (Ruby #inspect). It is a narrow slice of Runtime; a host can reuse the same implementation.

type MockNoMethodError

type MockNoMethodError struct{ Msg string }

MockNoMethodError is the Ruby NoMethodError Mock raises for an unmocked, undelegated method.

func (*MockNoMethodError) Error

func (e *MockNoMethodError) Error() string

Error implements error.

type Passthrough

type Passthrough struct{ Err error }

Passthrough wraps an exception that must abort the run rather than be recorded as a failure (NoMemoryError, SignalException, SystemExit). The host returns it from TestBody.Invoke; RunTest propagates it via the returned abort value.

func (*Passthrough) Error

func (p *Passthrough) Error() string

Error implements error.

type RaiseOutcome

type RaiseOutcome struct {
	Raised        bool
	ErrClass      string
	ErrMessage    string
	Backtrace     []string // already minitest-filtered by the host
	Matched       bool     // raised class is a member of exp
	IsAssertion   bool     // raised was Minitest::Assertion (incl Skip/UnexpectedError)
	IsPassthrough bool     // raised was SignalException or SystemExit
}

RaiseOutcome is what the host reports after running an assert_raises block: the block either raised an exception (Raised true) or returned normally. When it raised, ErrClass/ErrMessage/Backtrace describe it. The host classifies whether the raised class is one of the expected classes (Matched) and whether it was itself a Minitest Assertion/Skip (IsAssertion) or a SignalException/SystemExit (IsPassthrough), since those re-raise rather than fail.

type Reportable2

type Reportable2 interface {
	ResultLabel() string
	Message() string
	Location() string
}

Reportable2 is the minimal interface a failure exposes for result coding: a long label whose first letter is the single-character code. (Assertion, Skip, UnexpectedError all satisfy it.)

type Result

type Result struct {
	Klass      string
	TestName   string
	Assertions int
	// Failures holds Assertion/Skip/UnexpectedError values captured during the
	// run, in the order capture_exceptions appended them.
	Failures []Reportable2
	Time     float64
	// SourceFile/SourceLine mirror Result#source_location.
	SourceFile string
	SourceLine int
}

Result is Minitest::Result — a marshalable snapshot of one test run. It holds the class/name, assertion count, the captured failures (in occurrence order), and timing. Its rendering and predicates match the gem.

func (*Result) Errored

func (r *Result) Errored() bool

Errored reports whether any failure is an UnexpectedError (Reportable#error?).

func (*Result) Failure

func (r *Result) Failure() Reportable2

Failure returns the first captured failure, or nil (Reportable#failure).

func (*Result) Location

func (r *Result) Location(baseDir string) string

Location returns the test's location string (Reportable#location): "Class#name" plus " [file:line]" of the failure unless the run passed or errored. baseDir, when non-empty and a prefix of the failure location, is stripped (Ruby's delete_prefix BASE_DIR); the host passes Dir.pwd+"/".

func (*Result) Passed

func (r *Result) Passed() bool

Passed reports whether the run passed: no failure (Reportable#passed?). Skips are NOT passing.

func (*Result) ResultCode

func (r *Result) ResultCode() string

ResultCode returns ".", "F", "E", or "S" (Reportable#result_code): the failure's code, or "." when passed.

func (*Result) Skipped

func (r *Result) Skipped() bool

Skipped reports whether the (first) failure is a Skip (Reportable#skipped?).

func (*Result) String

func (r *Result) String(baseDir string) string

String renders Result#to_s: for a passed (non-skipped) run, just the location; otherwise each failure as "<label>:\n<location>:\n<message>\n", joined by "\n".

type Runtime

type Runtime interface {
	// Inspect returns obj.inspect — the Ruby #inspect string. This is the raw
	// material of mu_pp; the package layers encoding annotations and diff cleanup
	// on top of it (see [Assertions.MuPP]).
	Inspect(obj Value) string

	// Encoding returns the name of obj's string encoding (e.g. "UTF-8") and
	// whether the string is validly encoded. It is consulted by mu_pp only for
	// String values whose encoding differs from the default external encoding or
	// which are invalid. For non-strings the result is ignored.
	Encoding(obj Value) (name string, valid bool)

	// DefaultExternalEncoding returns Encoding.default_external's name. mu_pp
	// annotates a String whose encoding differs from this.
	DefaultExternalEncoding() string

	// IsString reports whether obj is a Ruby String (String === obj). Used by
	// mu_pp (encoding annotation) and by assert_match (string→regexp promotion).
	IsString(obj Value) bool

	// Equal reports obj == other (Ruby #==). Drives assert_equal / refute_equal.
	Equal(a, b Value) bool

	// Same reports a.equal?(b) — Ruby object identity. Drives assert_same /
	// refute_same.
	Same(a, b Value) bool

	// ObjectID returns obj.object_id, used verbatim in the assert_same /
	// refute_same failure message.
	ObjectID(obj Value) int64

	// Truthy reports Ruby truthiness: false only for nil and false.
	Truthy(obj Value) bool

	// IsNil reports obj.nil?.
	IsNil(obj Value) bool

	// Match returns the truthiness of (matcher =~ obj). The host evaluates the
	// Ruby =~ operator (Regexp#=~, String#=~, or a custom one). assert_match has
	// already promoted a bare String matcher to a Regexp before calling, by way
	// of [Runtime.StringToRegexp].
	Match(matcher, obj Value) bool

	// StringToRegexp builds Regexp.new(Regexp.escape(s)) for a String matcher, so
	// assert_match / refute_match match the gem's literal-string promotion.
	StringToRegexp(s Value) Value

	// RespondTo reports obj.respond_to?(meth, includeAll).
	RespondTo(obj Value, meth string, includeAll bool) bool

	// Includes reports collection.include?(obj).
	Includes(collection, obj Value) bool

	// Empty reports obj.empty?.
	Empty(obj Value) bool

	// InstanceOf reports obj.instance_of?(cls).
	InstanceOf(obj, cls Value) bool

	// KindOf reports obj.kind_of?(cls).
	KindOf(obj, cls Value) bool

	// ClassName returns obj.class.name — used in several messages (e.g.
	// assert_instance_of's "not #{obj.class}", assert_respond_to's "(#{obj.class})").
	ClassName(obj Value) string

	// Name returns the printable name of a class/module Value (its #to_s / #name),
	// used where the gem interpolates a bare class such as assert_instance_of's
	// "to be an instance of #{cls}".
	Name(cls Value) string

	// Send invokes obj.__send__(op, args...) and returns the result, used by
	// assert_operator / assert_predicate (and their refute_ twins). op is the
	// Ruby method name (e.g. "<=", "empty?").
	Send(obj Value, op string, args ...Value) Value
}

Runtime is the seam through which this package obtains genuine Ruby value semantics. The host (rbgo) implements it over its object graph. Every method corresponds to a Ruby message the assertion layer would otherwise have sent to a Ruby object; keeping them here makes the assertion logic pure and the value semantics the host's responsibility.

All methods must be deterministic for a given pair of arguments so the failure messages this package builds are reproducible.

type Skip

type Skip struct {
	Assertion
}

Skip is Minitest::Skip — the assertion raised by skip. It is an Assertion subclass whose result label is "Skipped".

func (*Skip) ResultCode

func (s *Skip) ResultCode() string

ResultCode returns "S".

func (*Skip) ResultLabel

func (s *Skip) ResultLabel() string

ResultLabel returns "Skipped".

type StubHarness

type StubHarness interface {
	// Install replaces the method named by the stub: it aliases the original
	// under "__minitest_stub__<name>" and defines a replacement that, when the
	// stub value responds to :call, calls it, else returns the value as-is
	// (yielding block_args to a passed block when given). Returns an error if the
	// method does not already exist (the gem requires it).
	Install() error
	// RunBlock runs the user block with the (possibly stubbed) receiver as its
	// argument (block[self]) and returns its error/exception, if any.
	RunBlock() error
	// Restore undoes Install: undef the replacement, alias the original back, and
	// undef the temp alias. It must run even when RunBlock errors (the ensure).
	Restore()
}

StubHarness is the seam the host implements to stub a single method on an object for the duration of a block (Object#stub). The library owns only the install → run → restore orchestration (the Ruby method's `ensure`); the host performs the actual singleton-method alias/define/undef and runs the user block.

type TestBody

type TestBody interface {
	// Invoke calls the named instance method (e.g. "setup", "test_foo"). It
	// returns the captured exception, already classified, or nil on success.
	Invoke(method string) error
	// Name is the name of the test method this instance runs (the Ruby `name`).
	Name() string
	// ClassName is the test's class name, for Result/location.
	ClassName() string
	// Assertions returns the current assertion count to record into the Result.
	Assertions() int
	// SourceLocation returns the ["file", line] of the test method (Result.from).
	SourceLocation() (string, int)
}

TestBody is the seam the host implements to run one test method instance: it invokes a named method (a setup/teardown hook or the test body) in the Ruby VM. Invoke returns nil if the method completed normally, or an error describing the exception it raised. The error must be an *Assertion / *Skip when an assertion failed or a skip was requested, an *UnexpectedError for any other Ruby exception, or one of the passthrough sentinels (see Passthrough) for NoMemoryError / SignalException / SystemExit, which abort the run.

type ThrowOutcome

type ThrowOutcome struct {
	Caught   bool
	OtherSym string // the symbol actually thrown, when different from sym
}

ThrowOutcome is what the host reports after an assert_throws block: whether the expected symbol was thrown (Caught), and, for the "not :other" message tail, the name of a different symbol that was thrown instead (OtherSym, "" if none).

type UnexpectedError

type UnexpectedError struct {
	Assertion
	// ErrorClass is the wrapped error's class name (e.g. "RuntimeError").
	ErrorClass string
	// ErrorMessage is the wrapped error's #message.
	ErrorMessage string
}

UnexpectedError is Minitest::UnexpectedError — wraps an error that was raised during a run but was not an Assertion (a genuine test error). Its message and backtrace come from the wrapped error; the host supplies ErrorClass / ErrorMessage / the (filtered) backtrace.

func (*UnexpectedError) Error

func (u *UnexpectedError) Error() string

Error implements error.

func (*UnexpectedError) Message

func (u *UnexpectedError) Message() string

Message renders the Minitest::UnexpectedError#message:

"#{error.class}: #{error.message}\n    #{filtered_backtrace.join("\n    ")}"

The host has already filtered the backtrace and stripped the Dir.pwd prefix from each frame (those are environment concerns); this method only joins.

func (*UnexpectedError) ResultCode

func (u *UnexpectedError) ResultCode() string

ResultCode returns "E".

func (*UnexpectedError) ResultLabel

func (u *UnexpectedError) ResultLabel() string

ResultLabel returns "Error".

type Value

type Value = any

Value is an opaque Ruby value handed across the seam. This package never inspects its concrete shape; it only passes it back to the Runtime for the Ruby-semantic operations (inspect, ==, =~, …). A host represents it however it likes (its own object pointer, an interface, etc.).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL