check

package module
v1.13.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 22 Imported by: 3

README

check

License MIT Go version Test Coverage Status Release Go Reference

Helpers to complement Go testing package.

Write tests with ease and fun!

Rationale

Plain Go tests force a choice between two extremes: verbose if got != want { t.Fatalf(...) } boilerplate for every comparison, or reaching for a heavier test framework that replaces go test with its own runner and vocabulary. This project avoids both:

  • It never introduces a new concept - no suites, no custom runner, no dot-import. Every wrapped value is still a real *testing.T/*testing.B/*testing.F underneath, so tt.Run, tt.Parallel, t.Log, t.Cleanup and everything else you already know keep working exactly as before.
  • Checkers already know what "nil", "equal" or "contains" means for the type you handed them, so you rarely write the type-specific comparison yourself: t.Nil(err) handles typed-nil pointers correctly (see the Nil doc comment for the classic gotcha), t.Len(v, 3) works the same for a map, slice, string, channel or array, t.Err(err, io.EOF) unwraps and compares by value instead of just by chain membership.
  • A failed check prints a full, readable dump of both values plus a text diff, so t.DeepEqual(got, want) on a whole struct/slice/map actually shows you what's wrong, instead of the got X, want Y you'd hand-roll around ==. See Failure Output below.
  • check.Must()/check.New() let you pick, per test, whether a failed check should stop the test (testify/require-like) or just record the failure and continue (testify/assert-like), without importing two different packages for that.
  • check.TestMain adds a per-test and grand-total pass/fail/todo counter to every run, so you get a sense of overall test health beyond individual PASS/FAIL lines - see the checks: lines in Failure Output.

[!NOTE]

The first check commit dates back to December 2017 — a time when testify had seen no activity for five months, with 40+ pull requests piling up and the organization's domain parked for sale (issue #526). testify has since recovered, but check was born from the uncertainty of that period and took a different approach from the start.

Features

  • Zero required external dependencies. Protobuf/gRPC comparison support pulls its (much heavier) dependencies in only if you opt into the companion submodules.
  • Compelling output from failed tests:
    • Very easy-to-read dumps for expected and actual values.
    • Same text diff you loved in testify.
  • Statistics with amount of passed/failed checks.
  • Colored output in terminal.
  • 100% compatible with testing package - check package just provides convenient wrappers for *testing.T/*testing.B/*testing.F methods without an unusual execution flow (see Non-goals).
  • All checks you may ever need! :)
  • Very easy to add your own check functions.
  • Concise, handy and consistent API, without dot-import!

Quickstart

Wrap each (including subtests) *testing.T/*testing.B/*testing.F using check.Must() and write tests as usually with testing package. Call new methods provided by this package to have more clean/concise test code and cool dump/diff.

check.Must() is the recommended default: it stops the test on the first failed check (like testify/require). Use check.New() instead for the softer, testify/assert-like behavior where a failed check doesn't stop the test.

[!NOTE]

Call tb.Run()/tb.Parallel() on the original *testing.T/*testing.B before wrapping it with check.Must()/check.New() — these two aren't available on the wrapped value (this also satisfies the paralleltest linter):

import "github.com/powerman/check"

func TestSomething(tt *testing.T) {
    tt.Parallel()
    t := check.Must(tt)
    t.Equal(2, 2)
    t.Log("You can use new t just like usual *testing.T")
    tt.Run("Subtests/Parallel example", func(tt *testing.T) {
        tt.Parallel()
        t := check.Must(tt)
        t.NotEqual(2, 3, "should not be 3!")
        obj, err := NewObj()
        if t.Nil(err) {
            t.Match(obj.field, `^\d+$`)
        }
    })
}

To get optional statistics about executed checkers add:

func TestMain(m *testing.M) { check.TestMain(m) }

See the package examples for more runnable snippets: table-driven subtests, soft checks with New, TODO, custom Should checkers, Err/ErrIs/ErrAs/Match side by side, and MergeContext.

Legacy check.T()

check.T(tt *testing.T) *check.C is the original, soft-mode by default constructor kept for backward compatibility - *check.C behaves exactly like it always did, including direct access to the wrapped *testing.T via its T field. New code should prefer check.New()/check.Must().

Installation

go get github.com/powerman/check

Failure Output

Here's what a failed DeepEqual looks like (from testdata/demo/demo_test.go, run with go test -tags demo -v ./testdata/demo/). Only Total actually differs - ID, Customer and Items match - so Diff singles out the one line that's wrong instead of making you eyeball two full dumps for what changed. The checks: lines at the end are check.TestMain's pass/fail/todo counters (one passing Equal plus this failing DeepEqual):

=== RUN   TestDemoFailure
    demo_test.go:41: order total should match after checkout
        Checker:  DeepEqual
        Expected: (demo.Order) {
          ID: (string) (len=3) "A-1",
          Customer: (string) (len=3) "Ann",
          Total: (int) 40,
          Items: ([]string) (len=2) {
            (string) (len=3) "pen",
            (string) (len=3) "cup"
          }
        }
        Actual:   (demo.Order) {
          ID: (string) (len=3) "A-1",
          Customer: (string) (len=3) "Ann",
          Total: (int) 42,
          Items: ([]string) (len=2) {
            (string) (len=3) "pen",
            (string) (len=3) "cup"
          }
        }

        Diff:
        --- Expected
        +++ Actual
        @@ -3,3 +3,3 @@
           Customer: (string) (len=3) "Ann",
        -  Total: (int) 40,
        +  Total: (int) 42,
           Items: ([]string) (len=2) {

--- FAIL: TestDemoFailure (0.00s)
  checks:  1 passed          1 failed	TestDemoFailure
  checks:  1 passed  0 todo  1 failed	(total)

With a color terminal (see doc.go for the FORCE_COLOR/NO_COLOR environment variables) the same failure looks like this:

Colored failure output

Custom Checkers

You can extend DeepEqual/NotDeepEqual and Err/NotErr with custom comparison logic via RegisterEqualChecker and RegisterErrChecker.

  • This package enables validator FieldError and []FieldError comparison by Namespace()+Tag() via check.Err/check.NotErr.
Protobuf / gRPC Support

Protobuf message comparison and gRPC status error comparison have been extracted into separate modules to keep the core dependency-light:

  • checkproto — enables proto.Equal via check.DeepEqual/check.NotDeepEqual for protobuf messages.
  • checkgrpc — enables gRPC status comparison via check.Err/check.NotErr. It also imports checkproto, so a single blank import covers both.

Usage: just add a blank import in your test file or TestMain:

import _ "github.com/powerman/checkgrpc"

Comparison

A few honest notes on how check compares to other assertion libraries, so you can pick the right one instead of the one you found first.

vs testify

testify's assert/require packages solve the same problem: convenience wrappers around *testing.T with a soft/hard mode split, and (via assert.New(t)/require.New(t)) a method-style API too, so that's not a real difference. Where check does differ:

  • Argument order: check and go-quicktest/qt put the actual value first, matching if got != want (t.Equal(got, want)) and the got = %v, want %v shape of Go's own idiomatic test failure messages. testify and shoenig/test put the expected value first instead (assert.Equal(t, want, got), must.Eq(t, want, got)), which only really makes sense if you're already used to it: got is usually a separate variable set by calling the code under test, often on its own long line, while want is often a short literal written inline - want, got order buries the value to check at the end of the call.
  • Dump + diff is the default failure output, not an opt-in - you don't need assert.EqualExportedValues or a separate diff helper to get a readable struct comparison.
  • No suite package, no mock package - check stays a pure assertion/checker library. Use testify/mock (or anything else) alongside it if you need mocks.

A few UX differences side by side:

  • Error is nil: t.Nil(err) vs assert.NoError(t, err) or assert.Nil(t, err) - two names for one idea.
  • Error unwraps to & value-equals a sentinel/custom error: t.Err(err, io.EOF) vs no direct equivalent - assert.ErrorIs only checks chain membership, not value equality of a freshly-constructed error.
  • Length of a map/slice/string/channel: t.Len(v, 3) vs assert.Len(t, v, 3), but len == 0 needs the separate assert.Empty/NotEmpty.

check does not cover 100% of testify's assert/require surface. Beyond Eventually/Never and the HTTP handler helpers (deliberately left out, see Non-goals), testify's IsIncreasing/IsDecreasing/IsNonIncreasing/IsNonDecreasing (sequence monotonicity) and YAMLEq have no check equivalent - not deliberately, just not needed yet. (Open an issue if you need them.) Everything else (Same, EqualValues, Positive, Negative, ...) is one line via an existing checker (t.Equal already compares pointers by identity, t.Greater(x, 0) covers Positive).

vs go-quicktest/qt and shoenig/test

go-quicktest/qt and shoenig/test are newer, generics-first libraries: qt.Assert(t, got, qt.Equals(want)) and must.Eq(t, want, got) are package-level generic functions, so a type mistake (comparing int to int32) is often a compile error instead of a runtime panic or failed check.

In practice that payoff is smaller than it sounds: a type mistake fails the test either way, the only question is whether you see it as a go build/go vet error before running go test (qt/shoenig/test) or as a panic/failed check while it runs (check) - not whether it's caught. check is deliberately not generic for a more concrete reason: part of its convenience only works with any (Match accepts string/[]byte/error/fmt.Stringer; Equal special-cases time.Time), and Go doesn't yet support generic methods, so a generic check.TB isn't possible without splitting the API into free functions. Pick qt or shoenig/test if you want compile-time typed assertions as package functions; pick check if you want a method-style API and check's dump/diff by default.

vs gotest.tools/v3

gotest.tools/v3/assert is close in spirit (wraps *testing.T-style helpers, diffs with go-cmp) but keeps the package-function shape (assert.Equal(t, x, y), assert.DeepEqual(t, x, y, opts...)) and leans on go-cmp's option system for custom comparisons, where check uses a small RegisterEqualChecker/RegisterErrChecker registry instead. If you already rely on go-cmp's options (unexported fields, custom comparers, ...), it plugs into check just as easily: register it once with check.RegisterEqualChecker(func(a, b any) (bool, bool) { return cmp.Equal(a, b), true }) to make it check's default, or call it ad hoc with t.True(cmp.Equal(got, want)) (no dump/diff for that one call, but full access to go-cmp's own options).

Non-goals

  • No BDD/suites - check stays inside go test, t.Run and t.Parallel; see Rationale.
  • No Eventually/Never polling helpers - Go's testing/synctest covers that class of test better (deterministic virtual time, no flaky sleeps).
  • No HTTP handler assertions (testify's HTTPSuccess/HTTPRedirect/HTTPBodyContains/...) - net/http/httptest plus check's own checkers already cover that ground, e.g. t.Match(rec.Body.String(), pattern) or t.Equal(rec.Code, http.StatusOK).
  • No mocking - pair check with whatever mocking library you already use.

TODO

  • Questionable:
    • Provide a way to force binary dump for utf8.Valid string/[]byte?
    • Count skipped tests (will have to overload Skip, Skipf, SkipNow)?
  • Complicated:
    • Show line of source_test.go with failed test.

Documentation

Overview

Package check provide helpers to complement Go testing package.

Features

This package is like testify/assert on steroids. :)

  • Compelling output from failed tests:
  • Very easy-to-read dumps for expected and actual values.
  • Same text diff you loved in testify/assert.
  • Statistics with amount of passed/failed checks.
  • Colored output in terminal.
  • 100% compatible with testing package - check package just provide convenient wrappers for *testing.T methods and doesn't introduce new concepts like BDD, custom test suite or unusual execution flow.
  • All checks you may ever need! :)
  • Very easy to add your own check functions.
  • Concise, handy and consistent API, without dot-import!

Quickstart

Wrap each (including subtests) *testing.T/*testing.B/*testing.F using Must and write tests as usually with testing package. Call new methods provided by this package to have more clean/concise test code and cool dump/diff.

Must stops the test on the first failed check (like testify/require). Use New instead for the softer, testify/assert-like behavior where a failed check doesn't stop the test.

import "github.com/powerman/check"

func TestSomething(tt *testing.T) {
	tt.Parallel()
	t := check.Must(tt)
	t.Equal(2, 2)
	t.Log("You can use new t just like usual *testing.T")
	tt.Run("Subtests/Parallel example", func(tt *testing.T) {
		tt.Parallel()
		t := check.Must(tt)
		t.NotEqual(2, 3, "should not be 3!")
		obj, err := NewObj()
		if t.Nil(err) {
			t.Match(obj.field, `^\d+$`)
		}
	})
}

To get optional statistics about executed checkers add:

func TestMain(m *testing.M) { check.TestMain(m) }

TB (returned by New/Must) doesn't provide Run/Parallel: call tb.Run()/tb.Parallel() on the original *testing.T/*testing.B/*testing.F before wrapping it (this also satisfies the paralleltest linter).

C (returned by the legacy T) is a soft-mode by default, *testing.T-only compatibility shell kept for old code: it behaves exactly like it always did, including direct access to the wrapped *testing.T via its T field, and does provide Run/Parallel. New code should prefer New/Must.

Hints

★ How to check for errors:

// If you just want nil:
t.Nil(err)
t.Err(err, nil)

// Check for (absence of) concrete (possibly wrapped) error:
t.Err(err, io.EOF)
t.NotErr(err, io.EOF) // nil is not io.EOF, so it's ok too

// When need to match by error's text:
t.Match(err, `file.*permission`)

// Use Equal ONLY when checking for same instance:
t.Equal(io.EOF, io.EOF)                // this works
t.Equal(io.EOF, errors.New("EOF"))     // this doesn't work!
t.Err(io.EOF, errors.New("EOF"))       // this works
t.DeepEqual(io.EOF, errors.New("EOF")) // this works too

// ErrIs/ErrAs are pure errors.Is/errors.As wrappers:
t.ErrIs(err, io.EOF)            // errors.Is(err, io.EOF)
t.ErrAs(err, &targetType)       // errors.As(err, &targetType)

When to use which:

  • Err — same type and value (unwraps to root, compares by value), support for extra custom error types (e.g. gRPC status or validator.FieldError)
  • ErrIs — standard errors.Is (not value comparison)
  • ErrAs — extract the first matching error type
  • Match — check by error text against a regexp

★ Each check returns bool, so you can easily skip problematic code:

if t.Nil(err) {
	t.Match(obj.field, `^\d+$`)
}

★ You can turn any soft (New, legacy T) check into assertion to stop test immediately:

t.Must(t.Nil(err))

★ You can turn all soft checks into assertions to stop test immediately (or just use Must):

t = t.MustAll()
t.Nil(err)

★ You can provide extra description to each check:

t.Equal(got, want, "Just msg: will Print(), % isn't special")
t.Equal(got, want, "Msg with args: will Printf(): %v", extra)

★ There are short synonyms for checks implementing usual ==, !=, etc.:

t.EQ(got, want) // same as t.Equal
t.NE(got, want) // same as t.NotEqual
t.LT(got, want) // same as t.Less
t.LE(got, want) // same as t.LessOrEqual
t.GT(got, want) // same as t.Greater
t.GE(got, want) // same as t.GreaterOrEqual

★ If you need custom check, which isn't available out-of-box - see [Should] checker, it'll let you plug in your own checker with ease.

★ It will panic when called with arg of wrong type - because this means bug in your test.

★ If you don't see colors in `go test` output it may happen because either you're not running in a terminal or your $TERM is set to "dumb" (or empty). To force colored output set one of these variables:

export FORCE_COLOR=1
export CLICOLOR_FORCE=1
export GO_TEST_COLOR=1

To disable colors (overrides all other variables):

export NO_COLOR=1

★ With the legacy T (whose C does provide Run/Parallel), if you use t.Parallel() inside a subtest, prefer calling tt.Parallel() on the original *testing.T before wrapping with check.T() — this satisfies the paralleltest linter:

t.Run("subtest", func(tt *testing.T) {
	tt.Parallel()
	t := check.T(tt)
	t.Equal(2, 2)
})

★ Inject an application base context (e.g. one carrying a slog handler) into a test on top of the per-test cancellation/deadline testing.TB.Context already provides:

t := check.Must(tt).MergeContext(appCtx)
t.Context() // merged values and cancellation from both contexts

★ Enable Protobuf message comparison and gRPC status error comparison by:

import _ "github.com/powerman/checkgrpc"

This enables proto.Equal for protobuf messages in [DeepEqual]/[NotDeepEqual] and gRPC status comparison in [Err]/[NotErr].

Contents

Constructors:

New   Must   T

Other special methods (assertion, context, custom checkers, etc.).

Context   MergeContext
Error     Errorf
Fatal     Fatalf
Fail      FailNow
Must      MustAll
Should
TODO

Everything else are just trivial (mostly) checkers which works in obvious way and accept values of any types which makes sense (and panics on everything else).

Nil             NotNil
Zero            NotZero
True            False

Equal           NotEqual           EQ  NE
DeepEqual       NotDeepEqual
Err             NotErr
ErrIs           NotErrIs
ErrAs           NotErrAs
BytesEqual      NotBytesEqual
JSONEqual

Greater         LessOrEqual        GT  LE
Less            GreaterOrEqual     LT  GE
Between         NotBetween
BetweenOrEqual  NotBetweenOrEqual
InDelta         NotInDelta
InSMAPE         NotInSMAPE

Len             NotLen
Match           NotMatch
HasPrefix       NotHasPrefix
HasSuffix       NotHasSuffix
HasKey          NotHasKey
Contains        NotContains
SortEqual       NotSortEqual
Subset          NotSubset

HasType         NotHasType
Implements      NotImplements

FileExists      NotFileExists
DirExists       NotDirExists

Panic           NotPanic
PanicMatch      PanicNotMatch
Example (ErrorChecks)

Example_errorChecks contrasts the four ways to check an error, see package doc for when to use which.

package main

import (
	"fmt"
	"io"
	"io/fs"
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	t := check.Must(tt)

	wrapped := fmt.Errorf("wrap: %w", io.EOF)

	// Err: unwraps to the root cause and compares by value.
	t.Err(wrapped, io.EOF)

	// ErrIs: pure errors.Is chain membership, no value comparison.
	t.ErrIs(wrapped, io.EOF)

	// ErrAs: extract the first error of a given type from the chain.
	var pathErr *fs.PathError
	if t.ErrAs(wrapped, &pathErr) {
		t.NotNil(pathErr)
	}

	// Match: check by error text against a regexp.
	t.Match(wrapped, `EOF$`)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckFieldError added in v1.12.0

func CheckFieldError(actual, expected error) (equal, ok bool)

CheckFieldError compares two errors that (possibly after errors.As-style unwrapping) are validator.FieldError-like values or slices of them, by an unordered multiset of (Namespace, Tag) pairs — order does not matter. Works with any type structurally providing Namespace()/Tag().

Auto-registered on loading the package.

func RegisterEqualChecker added in v1.12.0

func RegisterEqualChecker(f EqualChecker)

RegisterEqualChecker adds a custom equal comparison strategy used by DeepEqual and NotDeepEqual. Checkers run in registration order before built-in logic.

Intended to be called from init() or TestMain. Not safe to call concurrently with running checks.

func RegisterErrChecker added in v1.12.0

func RegisterErrChecker(f ErrChecker)

RegisterErrChecker adds a custom error comparison strategy used by Err and NotErr. Checkers run in registration order before built-in logic.

Intended to be called from init() or TestMain. Not safe to call concurrently with running checks.

func Report

func Report()

Report output statistics about passed/failed checks to stderr. It should be called from TestMain after m.Run(), for ex.:

func TestMain(m *testing.M) {
	code := m.Run()
	check.Report()
	os.Exit(code)
}

If this is all you need - just use TestMain instead.

Using stderr ensures the output does not interfere with `go test -json` (which expects only valid JSON on stdout).

func ResetEqualCheckers added in v1.12.0

func ResetEqualCheckers()

ResetEqualCheckers removes all registered equal checkers.

Combine with RegisterEqualChecker to define a custom chain in a specific order.

Intended for TestMain. Not safe to call concurrently with running checks.

func ResetErrCheckers added in v1.12.0

func ResetErrCheckers()

ResetErrCheckers removes all registered error checkers, including the built-in CheckFieldError.

Combine with RegisterErrChecker to define a custom chain in a specific order.

Intended for TestMain. Not safe to call concurrently with running checks.

func TestMain

func TestMain(m *testing.M)

TestMain provides same default implementation as used by testing package with extra Report call to output statistics to stderr. Usage:

func TestMain(m *testing.M) { check.TestMain(m) }

Using stderr ensures the statistics output does not interfere with `go test -json` (which expects only valid JSON on stdout).

Types

type C added in v1.0.0

type C struct {
	*testing.T
	// contains filtered or unexported fields
}

C wraps *testing.T to make it convenient to call checkers in test.

func T

func T(tt *testing.T) *C

T creates and returns new *C, which wraps given tt and supposed to be used inplace of it, providing you with access to many useful helpers in addition to standard methods of *testing.T.

It's convenient to rename Test function's arg from t to something else, create wrapped variable with usual name t and use only t:

func TestSomething(tt *testing.T) {
	t := check.T(tt)
	// use only t in test and don't touch tt anymore
}

T is a soft-mode, *testing.T-only legacy constructor kept for backward compatibility. For new tests prefer Must, which also works with *testing.B and *testing.F.

func (C) Between added in v1.0.0

func (t C) Between(actual, minimum, maximum any, msg ...any) bool

Between checks for min < actual < max.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) BetweenOrEqual added in v1.0.0

func (t C) BetweenOrEqual(actual, minimum, maximum any, msg ...any) bool

BetweenOrEqual checks for min <= actual <= max.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) BytesEqual added in v1.0.0

func (t C) BytesEqual(actual, expected []byte, msg ...any) bool

BytesEqual checks for bytes.Equal(actual, expected).

Hint: BytesEqual([]byte{}, []byte(nil)) is true (unlike DeepEqual).

func (C) Contains added in v1.0.0

func (t C) Contains(actual, expected any, msg ...any) bool

Contains checks is actual contains substring/element expected.

Element of array/slice/map is checked using == expected.

Type of expected depends on type of actual:

  • if actual is a string, then expected should be a string
  • if actual is an array, then expected should have array's element type
  • if actual is a slice, then expected should have slice's element type
  • if actual is a map, then expected should have map's value type

Hint: In a map it looks for a value, if you need to look for a key - use HasKey instead.

func (*C) Context added in v1.13.0

func (t *C) Context() context.Context

Context returns the context associated with t: the context merged in by the most recent C.MergeContext call if any, otherwise the standard *testing.T.Context().

func (C) DeepEqual added in v1.0.0

func (t C) DeepEqual(actual, expected any, msg ...any) bool

DeepEqual checks for deepequal.DeepEqual(actual, expected). It will use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.).

Custom equal checkers registered via RegisterEqualChecker run first.

func (C) DirExists added in v1.13.0

func (t C) DirExists(path string, msg ...any) bool

DirExists checks that path exists and is a directory.

See FileExists about Stat error handling.

func (C) EQ added in v1.0.0

func (t C) EQ(actual, expected any, msg ...any) bool

EQ is a synonym for Equal.

func (C) Equal added in v1.0.0

func (t C) Equal(actual, expected any, msg ...any) bool

Equal checks for actual == expected.

Note: For time.Time it uses actual.Equal(expected) instead.

func (C) Err added in v1.0.0

func (t C) Err(actual, expected error, msg ...any) bool

Err checks is actual error is the same as expected error.

Custom error checkers registered via RegisterErrChecker run first. If none claims the pair the built-in comparison operates on the original error found by recursively unwrapping actual with errors.Unwrap() and github.com/pkg/errors.Cause() (multi-error takes only the first), and then compares it using Equal() method or same type and value (deepequal.DeepEqual), so they may be different instances, but must have the same type and value.

If both of these fail the comparison falls back to errors.Is() on the original actual (not the unwrapped one).

Checking for nil is okay, but using Nil(actual) instead is more clean.

func (C) ErrAs added in v1.12.0

func (t C) ErrAs(actual error, target any, msg ...any) bool

ErrAs checks for errors.As.

target must be a non-nil pointer to an error type or to an interface, as required by errors.As. On success target is filled with the matched error value. See errors.As documentation for details.

func (C) ErrIs added in v1.12.0

func (t C) ErrIs(actual, expected error, msg ...any) bool

ErrIs checks for errors.Is().

Unlike Err which tries to unwrap to root cause and compare values, ErrIs uses pure errors.Is semantics for exact error matching.

See Err for value-equality checks. ErrIs is preferred when you want the standard Go unwrapping semantics without value comparison.

func (*C) Error added in v1.4.0

func (t *C) Error(args ...any)

Error is equivalent to Log followed by Fail.

It is like t.Errorf with TODO() and statistics support.

func (*C) Errorf added in v1.10.0

func (t *C) Errorf(format string, args ...any)

Errorf is equivalent to Logf followed by Fail.

It is like t.Errorf with TODO() and statistics support.

func (*C) Fail added in v1.13.0

func (t *C) Fail()

Fail marks the function as having failed but continues execution.

Unlike plain *testing.T.Fail, calling it directly (rather than through a checker) is still counted in check's pass/fail statistics.

func (*C) FailNow added in v1.13.0

func (t *C) FailNow()

FailNow marks the function as having failed and stops its execution.

Unlike plain *testing.T.FailNow, calling it directly (rather than through a checker) is still counted in check's pass/fail statistics.

func (C) False added in v1.0.0

func (t C) False(cond bool, msg ...any) bool

False checks for cond == false.

func (*C) Fatal added in v1.10.0

func (t *C) Fatal(args ...any)

Fatal is equivalent to Log followed by FailNow.

It is like t.Fatal with TODO() and statistics support.

func (*C) Fatalf added in v1.10.0

func (t *C) Fatalf(format string, args ...any)

Fatalf is equivalent to Logf followed by FailNow.

It is like t.Fatalf with TODO() and statistics support.

func (C) FileExists added in v1.13.0

func (t C) FileExists(path string, msg ...any) bool

FileExists checks that path exists and is not a directory.

A Stat error other than "not exists" (e.g. permission denied) counts as "does not exist", same as testify.

func (C) GE added in v1.0.0

func (t C) GE(actual, expected any, msg ...any) bool

GE is a synonym for GreaterOrEqual.

func (C) GT added in v1.0.0

func (t C) GT(actual, expected any, msg ...any) bool

GT is a synonym for Greater.

func (C) Greater added in v1.0.0

func (t C) Greater(actual, expected any, msg ...any) bool

Greater checks for actual > expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) GreaterOrEqual added in v1.0.0

func (t C) GreaterOrEqual(actual, expected any, msg ...any) bool

GreaterOrEqual checks for actual >= expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) HasKey added in v1.0.0

func (t C) HasKey(actual, expected any, msg ...any) bool

HasKey checks is actual has key expected.

func (C) HasPrefix added in v1.0.0

func (t C) HasPrefix(actual, expected any, msg ...any) bool

HasPrefix checks for strings.HasPrefix(actual, expected).

Both actual and expected may have any of these types:

  • string - will use as is
  • []byte - will convert with string()
  • []rune - will convert with string()
  • fmt.Stringer - will convert with actual.String()
  • error - will convert with actual.Error()
  • nil - check will always fail

func (C) HasSuffix added in v1.0.0

func (t C) HasSuffix(actual, expected any, msg ...any) bool

HasSuffix checks for strings.HasSuffix(actual, expected).

Both actual and expected may have any of these types:

  • string - will use as is
  • []byte - will convert with string()
  • []rune - will convert with string()
  • fmt.Stringer - will convert with actual.String()
  • error - will convert with actual.Error()
  • nil - check will always fail

func (C) HasType added in v1.0.0

func (t C) HasType(actual, expected any, msg ...any) bool

HasType checks is actual has same type as expected.

func (C) Implements added in v1.0.0

func (t C) Implements(actual, expected any, msg ...any) bool

Implements checks is actual implements interface pointed by expected.

You must use pointer to interface type in expected:

t.Implements(os.Stdin, (*io.Reader)(nil))

func (C) InDelta added in v1.0.0

func (t C) InDelta(actual, expected, delta any, msg ...any) bool

InDelta checks for expected-delta <= actual <= expected+delta.

All three actual, expected and delta must be either:

func (C) InSMAPE added in v1.0.0

func (t C) InSMAPE(actual, expected any, smape float64, msg ...any) bool

InSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is less than given smape.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats

Allowed smape values are: 0.0 < smape < 100.0.

Used formula returns SMAPE value between 0 and 100 (percents):

  • 0.0 when actual == expected
  • ~0.5 when they differs in ~1%
  • ~5 when they differs in ~10%
  • ~20 when they differs in 1.5 times
  • ~33 when they differs in 2 times
  • 50.0 when they differs in 3 times
  • ~82 when they differs in 10 times
  • 99.0+ when actual and expected differs in 200+ times
  • 100.0 when only one of actual or expected is 0 or one of them is positive while another is negative

func (C) JSONEqual added in v1.0.0

func (t C) JSONEqual(actual, expected any, msg ...any) bool

JSONEqual normalize formatting of actual and expected (if they're valid JSON) and then checks for bytes.Equal(actual, expected).

Both actual and expected may have any of these types:

In case any of actual or expected is nil or empty or (for string or []byte) is invalid JSON - check will fail.

func (C) LE added in v1.0.0

func (t C) LE(actual, expected any, msg ...any) bool

LE is a synonym for LessOrEqual.

func (C) LT added in v1.0.0

func (t C) LT(actual, expected any, msg ...any) bool

LT is a synonym for Less.

func (C) Len added in v1.0.0

func (t C) Len(actual any, expected int, msg ...any) bool

Len checks is len(actual) == expected.

func (C) Less added in v1.0.0

func (t C) Less(actual, expected any, msg ...any) bool

Less checks for actual < expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) LessOrEqual added in v1.0.0

func (t C) LessOrEqual(actual, expected any, msg ...any) bool

LessOrEqual checks for actual <= expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) Match added in v1.0.0

func (t C) Match(actual, regex any, msg ...any) bool

Match checks for regex.MatchString(actual).

Regex type can be either *regexp.Regexp or string.

Actual type can be:

  • string - will match with actual
  • []byte - will match with string(actual)
  • []rune - will match with string(actual)
  • fmt.Stringer - will match with actual.String()
  • error - will match with actual.Error()
  • nil - will not match (even with empty regex)

func (*C) MergeContext added in v1.13.0

func (t *C) MergeContext(ctx context.Context) *C

MergeContext is like TB.MergeContext, but keeps working with *C and *testing.T.

func (C) Must added in v1.0.0

func (c C) Must(continueTest bool, msg ...any)

Must interrupt test using t.FailNow if called with false value.

This provides an easy way to turn any check into assertion:

t.Must(t.Nil(err))

func (*C) MustAll added in v1.5.0

func (t *C) MustAll() *C

MustAll is like TB.MustAll, but keeps working with *C and *testing.T.

func (C) NE added in v1.0.0

func (t C) NE(actual, expected any, msg ...any) bool

NE is a synonym for NotEqual.

func (C) Nil added in v1.0.0

func (t C) Nil(actual any, msg ...any) bool

Nil checks for actual == nil.

There is one subtle difference between this check and Go `== nil` (if this surprises you then you should read https://golang.org/doc/faq#nil_error first):

var intPtr *int
var empty interface{}
var notEmpty interface{} = intPtr
t.True(intPtr == nil)   // TRUE
t.True(empty == nil)    // TRUE
t.True(notEmpty == nil) // FALSE

When you call this function your actual value will be stored in interface{} argument, and this makes any typed nil pointer value `!= nil` inside this function (just like in example above happens with notEmpty variable).

As it is very common case to check some typed pointer using Nil this check has to work around and detect nil even if usual `== nil` return false. But this has nasty side effect: if actual value already was of interface type and contains some typed nil pointer (which is usually bad thing and should be avoid) then Nil check will pass (which may be not what you want/expect):

t.Nil(nil)              // TRUE
t.Nil(intPtr)           // TRUE
t.Nil(empty)            // TRUE
t.Nil(notEmpty)         // WARNING: also TRUE!

Second subtle case is less usual: uintptr(0) is sorta nil, but not really, so Nil(uintptr(0)) will fail. Nil(unsafe.Pointer(nil)) will also fail, for the same reason. Please do not use this and consider this behaviour undefined, because it may change in the future.

func (C) NotBetween added in v1.0.0

func (t C) NotBetween(actual, minimum, maximum any, msg ...any) bool

NotBetween checks for actual <= min or max <= actual.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) NotBetweenOrEqual added in v1.0.0

func (t C) NotBetweenOrEqual(actual, minimum, maximum any, msg ...any) bool

NotBetweenOrEqual checks for actual < min or max < actual.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (C) NotBytesEqual added in v1.0.0

func (t C) NotBytesEqual(actual, expected []byte, msg ...any) bool

NotBytesEqual checks for !bytes.Equal(actual, expected).

Hint: NotBytesEqual([]byte{}, []byte(nil)) is false (unlike NotDeepEqual).

func (C) NotContains added in v1.0.0

func (t C) NotContains(actual, expected any, msg ...any) bool

NotContains checks is actual not contains substring/element expected.

See Contains about supported actual/expected types and check logic.

func (C) NotDeepEqual added in v1.0.0

func (t C) NotDeepEqual(actual, expected any, msg ...any) bool

NotDeepEqual checks for !deepequal.DeepEqual(actual, expected). It will use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.).

Custom equal checkers registered via RegisterEqualChecker run first.

func (C) NotDirExists added in v1.13.0

func (t C) NotDirExists(path string, msg ...any) bool

NotDirExists checks that path does not exist or is not a directory.

See FileExists about Stat error handling.

func (C) NotEqual added in v1.0.0

func (t C) NotEqual(actual, expected any, msg ...any) bool

NotEqual checks for actual != expected.

func (C) NotErr added in v1.0.0

func (t C) NotErr(actual, expected error, msg ...any) bool

NotErr checks is actual error is not the same as expected error.

It tries to recursively unwrap actual before checking using errors.Unwrap() and github.com/pkg/errors.Cause(). In case of multi-error (Unwrap() []error) it use only first error.

They must have either different types or values (or one should be nil). Different instances with same type and value will be considered the same error, and so is both nil.

Finally it'll use !errors.Is().

func (C) NotErrAs added in v1.12.0

func (t C) NotErrAs(actual error, target any, msg ...any) bool

NotErrAs checks for !errors.As.

target must be a non-nil pointer to an error type or to an interface, as required by errors.As. Note that errors.As may still fill target with a matched error even when this check returns true, because errors.As is always called regardless of the negated result.

func (C) NotErrIs added in v1.12.0

func (t C) NotErrIs(actual, expected error, msg ...any) bool

NotErrIs checks for !errors.Is().

See ErrIs for details. Note that nil is not matched by errors.Is against any non-nil error, so NotErrIs(nil, io.EOF) passes.

func (C) NotFileExists added in v1.13.0

func (t C) NotFileExists(path string, msg ...any) bool

NotFileExists checks that path does not exist or is a directory.

See FileExists about Stat error handling.

func (C) NotHasKey added in v1.0.0

func (t C) NotHasKey(actual, expected any, msg ...any) bool

NotHasKey checks is actual has no key expected.

func (C) NotHasPrefix added in v1.0.0

func (t C) NotHasPrefix(actual, expected any, msg ...any) bool

NotHasPrefix checks for !strings.HasPrefix(actual, expected).

See HasPrefix about supported actual/expected types and check logic.

func (C) NotHasSuffix added in v1.0.0

func (t C) NotHasSuffix(actual, expected any, msg ...any) bool

NotHasSuffix checks for !strings.HasSuffix(actual, expected).

See HasSuffix about supported actual/expected types and check logic.

func (C) NotHasType added in v1.0.0

func (t C) NotHasType(actual, expected any, msg ...any) bool

NotHasType checks is actual has not same type as expected.

func (C) NotImplements added in v1.0.0

func (t C) NotImplements(actual, expected any, msg ...any) bool

NotImplements checks is actual does not implements interface pointed by expected.

You must use pointer to interface type in expected:

t.NotImplements(os.Stdin, (*fmt.Stringer)(nil))

func (C) NotInDelta added in v1.0.0

func (t C) NotInDelta(actual, expected, delta any, msg ...any) bool

NotInDelta checks for actual < expected-delta or expected+delta < actual.

All three actual, expected and delta must be either:

func (C) NotInSMAPE added in v1.0.0

func (t C) NotInSMAPE(actual, expected any, smape float64, msg ...any) bool

NotInSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is greater than or equal to given smape.

See InSMAPE about supported actual/expected types and check logic.

func (C) NotLen added in v1.0.0

func (t C) NotLen(actual any, expected int, msg ...any) bool

NotLen checks is len(actual) != expected.

func (C) NotMatch added in v1.0.0

func (t C) NotMatch(actual, regex any, msg ...any) bool

NotMatch checks for !regex.MatchString(actual).

See Match about supported actual/regex types and check logic.

func (C) NotNil added in v1.0.0

func (t C) NotNil(actual any, msg ...any) bool

NotNil checks for actual != nil.

See Nil about subtle case in check logic.

func (C) NotPanic added in v1.0.0

func (t C) NotPanic(actual func(), msg ...any) bool

NotPanic checks is actual() don't panics.

It is able to detect panic(nil)… but you should try to avoid using this.

func (C) NotSortEqual added in v1.13.0

func (t C) NotSortEqual(actual, expected any, msg ...any) bool

NotSortEqual checks !SortEqual(actual, expected).

See SortEqual about supported actual/expected types and check logic.

func (C) NotSubset added in v1.13.0

func (t C) NotSubset(actual, expected any, msg ...any) bool

NotSubset checks !Subset(actual, expected).

See Subset about supported actual/expected types and check logic.

func (C) NotZero added in v1.0.0

func (t C) NotZero(actual any, msg ...any) bool

NotZero checks is actual is not zero value of it's type.

func (C) Panic added in v1.0.0

func (t C) Panic(actual func(), msg ...any) bool

Panic checks is actual() panics.

It is able to detect panic(nil)… but you should try to avoid using this.

func (C) PanicMatch added in v1.0.0

func (t C) PanicMatch(actual func(), regex any, msg ...any) bool

PanicMatch checks is actual() panics and panic text match regex.

Regex type can be either *regexp.Regexp or string.

In case of panic(nil) it will match like panic("<nil>").

func (C) PanicNotMatch added in v1.0.0

func (t C) PanicNotMatch(actual func(), regex any, msg ...any) bool

PanicNotMatch checks is actual() panics and panic text not match regex.

Regex type can be either *regexp.Regexp or string.

In case of panic(nil) it will match like panic("<nil>").

func (*C) Should added in v1.0.0

func (t *C) Should(anyShouldFunc any, args ...any) bool

Should is like TB.Should, but keeps working with *C and *testing.T.

ShouldFunc1/ShouldFunc2 callbacks always receive a *TB (never *C): there's only one pair of callback types, shared by TB and C alike.

func (C) SortEqual added in v1.13.0

func (t C) SortEqual(actual, expected any, msg ...any) bool

SortEqual checks that actual and expected contain the same elements, ignoring order (multiset equality, duplicates counted).

Both actual and expected must be slices or arrays. Elements need not be sortable and are compared like DeepEqual. Nil and empty slices are equal (like BytesEqual, unlike DeepEqual).

func (C) Subset added in v1.13.0

func (t C) Subset(actual, expected any, msg ...any) bool

Subset checks that actual contains all elements of expected: for slices/arrays - as multisets (duplicates counted), ignoring order; for maps - every key of expected exists in actual with an equal value.

actual and expected must both be slices/arrays or both be maps. Elements/values are compared like DeepEqual. An empty/nil expected is a subset of anything of the same kind.

Note: unlike testify's Subset, duplicates are counted, so [1,1] is not a subset of [1].

func (*C) TODO added in v1.0.0

func (t *C) TODO() *C

TODO is like TB.TODO, but keeps working with *C and *testing.T.

func (C) True added in v1.0.0

func (t C) True(cond bool, msg ...any) bool

True checks for cond == true.

This can be useful to use your own custom checks, but this way you won't get nice dump/diff for actual/expected values. You'll still have statistics about passed/failed checks and it's shorter than usual:

if !cond {
	t.Errorf(msg...)
}

func (C) Zero added in v1.0.0

func (t C) Zero(actual any, msg ...any) bool

Zero checks is actual is zero value of it's type.

type EqualChecker added in v1.12.0

type EqualChecker func(actual, expected any) (equal, ok bool)

EqualChecker compares two values for DeepEqual/NotDeepEqual. ok=false means "this checker does not apply to this pair" and the next registered checker (then the built-in logic) is consulted.

type ErrChecker added in v1.12.0

type ErrChecker func(actual, expected error) (equal, ok bool)

ErrChecker compares actual and expected errors. ok=false means "this checker does not apply to this pair" and the next registered checker (then the built-in logic) is consulted.

type ShouldFunc1

type ShouldFunc1 func(t *TB, actual any) bool

ShouldFunc1 is like Nil or Zero.

type ShouldFunc2

type ShouldFunc2 func(t *TB, actual, expected any) bool

ShouldFunc2 is like Equal or Match.

type TB added in v1.13.0

type TB struct {
	testing.TB
	// contains filtered or unexported fields
}

TB wraps testing.TB to make it convenient to call checkers in tests, benchmarks and fuzz targets.

Use New or Must to create it. C is a thin, *testing.T-only compatibility shell built on top of the same machinery.

func Must added in v1.13.0

func Must(tb testing.TB) *TB

Must creates and returns new *TB like New, but every failed check will interrupt the test using TB.FailNow.

This is the recommended default constructor for new tests.

Example

ExampleMust shows the recommended way to wrap a *testing.T: any failed check stops the test immediately, like testify/require.

package main

import (
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	tt.Parallel()
	t := check.Must(tt)

	t.Equal(2+2, 4)
	t.Match("build-42", `^build-\d+$`)
}
Example (TableDriven)

ExampleMust_tableDriven shows the usual table-driven pattern: wrap each subtest's own *testing.T inside tt.Run, not the outer one.

package main

import (
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	tt.Parallel()
	t := check.Must(tt)
	t.True(true, "outer setup check")

	cases := []struct {
		name string
		got  int
		want int
	}{
		{"add one", 1 + 1, 2},
		{"add two", 2 + 2, 4},
	}
	for _, c := range cases {
		tt.Run(c.name, func(tt *testing.T) {
			tt.Parallel()
			t := check.Must(tt)
			t.Equal(c.got, c.want)
		})
	}
}

func New added in v1.13.0

func New(tb testing.TB) *TB

New creates and returns new *TB, which wraps given tb and supposed to be used inplace of it, providing you with access to many useful helpers in addition to standard methods of testing.TB.

A failed check does not stop the test - use TB.MustAll or TB.Must(continueTest) to turn checks into assertions. See Must for a fail-fast alternative.

TB doesn't provide Run/Parallel: call tb.Run/tb.Parallel on the original *testing.T/*testing.B/*testing.F.

Example

ExampleNew shows the softer, testify/assert-like alternative to Must: a failed check doesn't stop the test, so guard dependent checks with the bool every checker returns.

package main

import (
	"os"
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	t := check.New(tt)

	obj, err := os.Open(os.DevNull)
	if t.Nil(err) {
		t.NotNil(obj)
		_ = obj.Close()
	}
}

func (TB) Between added in v1.13.0

func (t TB) Between(actual, minimum, maximum any, msg ...any) bool

Between checks for min < actual < max.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) BetweenOrEqual added in v1.13.0

func (t TB) BetweenOrEqual(actual, minimum, maximum any, msg ...any) bool

BetweenOrEqual checks for min <= actual <= max.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) BytesEqual added in v1.13.0

func (t TB) BytesEqual(actual, expected []byte, msg ...any) bool

BytesEqual checks for bytes.Equal(actual, expected).

Hint: BytesEqual([]byte{}, []byte(nil)) is true (unlike DeepEqual).

func (TB) Contains added in v1.13.0

func (t TB) Contains(actual, expected any, msg ...any) bool

Contains checks is actual contains substring/element expected.

Element of array/slice/map is checked using == expected.

Type of expected depends on type of actual:

  • if actual is a string, then expected should be a string
  • if actual is an array, then expected should have array's element type
  • if actual is a slice, then expected should have slice's element type
  • if actual is a map, then expected should have map's value type

Hint: In a map it looks for a value, if you need to look for a key - use HasKey instead.

func (*TB) Context added in v1.13.0

func (t *TB) Context() context.Context

Context returns the context associated with t: the context merged in by the most recent TB.MergeContext call if any, otherwise the standard testing.TB.Context().

func (TB) DeepEqual added in v1.13.0

func (t TB) DeepEqual(actual, expected any, msg ...any) bool

DeepEqual checks for deepequal.DeepEqual(actual, expected). It will use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.).

Custom equal checkers registered via RegisterEqualChecker run first.

func (TB) DirExists added in v1.13.0

func (t TB) DirExists(path string, msg ...any) bool

DirExists checks that path exists and is a directory.

See FileExists about Stat error handling.

func (TB) EQ added in v1.13.0

func (t TB) EQ(actual, expected any, msg ...any) bool

EQ is a synonym for Equal.

func (TB) Equal added in v1.13.0

func (t TB) Equal(actual, expected any, msg ...any) bool

Equal checks for actual == expected.

Note: For time.Time it uses actual.Equal(expected) instead.

func (TB) Err added in v1.13.0

func (t TB) Err(actual, expected error, msg ...any) bool

Err checks is actual error is the same as expected error.

Custom error checkers registered via RegisterErrChecker run first. If none claims the pair the built-in comparison operates on the original error found by recursively unwrapping actual with errors.Unwrap() and github.com/pkg/errors.Cause() (multi-error takes only the first), and then compares it using Equal() method or same type and value (deepequal.DeepEqual), so they may be different instances, but must have the same type and value.

If both of these fail the comparison falls back to errors.Is() on the original actual (not the unwrapped one).

Checking for nil is okay, but using Nil(actual) instead is more clean.

func (TB) ErrAs added in v1.13.0

func (t TB) ErrAs(actual error, target any, msg ...any) bool

ErrAs checks for errors.As.

target must be a non-nil pointer to an error type or to an interface, as required by errors.As. On success target is filled with the matched error value. See errors.As documentation for details.

func (TB) ErrIs added in v1.13.0

func (t TB) ErrIs(actual, expected error, msg ...any) bool

ErrIs checks for errors.Is().

Unlike Err which tries to unwrap to root cause and compare values, ErrIs uses pure errors.Is semantics for exact error matching.

See Err for value-equality checks. ErrIs is preferred when you want the standard Go unwrapping semantics without value comparison.

func (*TB) Error added in v1.13.0

func (t *TB) Error(args ...any)

Error is equivalent to Log followed by Fail.

It is like t.Errorf with TODO() and statistics support.

func (*TB) Errorf added in v1.13.0

func (t *TB) Errorf(format string, args ...any)

Errorf is equivalent to Logf followed by Fail.

It is like t.Errorf with TODO() and statistics support.

func (*TB) Fail added in v1.13.0

func (t *TB) Fail()

Fail marks the function as having failed but continues execution.

Unlike plain testing.TB.Fail, calling it directly (rather than through a checker) is still counted in check's pass/fail statistics.

func (*TB) FailNow added in v1.13.0

func (t *TB) FailNow()

FailNow marks the function as having failed and stops its execution.

Unlike plain testing.TB.FailNow, calling it directly (rather than through a checker) is still counted in check's pass/fail statistics.

func (TB) False added in v1.13.0

func (t TB) False(cond bool, msg ...any) bool

False checks for cond == false.

func (*TB) Fatal added in v1.13.0

func (t *TB) Fatal(args ...any)

Fatal is equivalent to Log followed by FailNow.

It is like t.Fatal with TODO() and statistics support.

func (*TB) Fatalf added in v1.13.0

func (t *TB) Fatalf(format string, args ...any)

Fatalf is equivalent to Logf followed by FailNow.

It is like t.Fatalf with TODO() and statistics support.

func (TB) FileExists added in v1.13.0

func (t TB) FileExists(path string, msg ...any) bool

FileExists checks that path exists and is not a directory.

A Stat error other than "not exists" (e.g. permission denied) counts as "does not exist", same as testify.

func (TB) GE added in v1.13.0

func (t TB) GE(actual, expected any, msg ...any) bool

GE is a synonym for GreaterOrEqual.

func (TB) GT added in v1.13.0

func (t TB) GT(actual, expected any, msg ...any) bool

GT is a synonym for Greater.

func (TB) Greater added in v1.13.0

func (t TB) Greater(actual, expected any, msg ...any) bool

Greater checks for actual > expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) GreaterOrEqual added in v1.13.0

func (t TB) GreaterOrEqual(actual, expected any, msg ...any) bool

GreaterOrEqual checks for actual >= expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) HasKey added in v1.13.0

func (t TB) HasKey(actual, expected any, msg ...any) bool

HasKey checks is actual has key expected.

func (TB) HasPrefix added in v1.13.0

func (t TB) HasPrefix(actual, expected any, msg ...any) bool

HasPrefix checks for strings.HasPrefix(actual, expected).

Both actual and expected may have any of these types:

  • string - will use as is
  • []byte - will convert with string()
  • []rune - will convert with string()
  • fmt.Stringer - will convert with actual.String()
  • error - will convert with actual.Error()
  • nil - check will always fail

func (TB) HasSuffix added in v1.13.0

func (t TB) HasSuffix(actual, expected any, msg ...any) bool

HasSuffix checks for strings.HasSuffix(actual, expected).

Both actual and expected may have any of these types:

  • string - will use as is
  • []byte - will convert with string()
  • []rune - will convert with string()
  • fmt.Stringer - will convert with actual.String()
  • error - will convert with actual.Error()
  • nil - check will always fail

func (TB) HasType added in v1.13.0

func (t TB) HasType(actual, expected any, msg ...any) bool

HasType checks is actual has same type as expected.

func (TB) Implements added in v1.13.0

func (t TB) Implements(actual, expected any, msg ...any) bool

Implements checks is actual implements interface pointed by expected.

You must use pointer to interface type in expected:

t.Implements(os.Stdin, (*io.Reader)(nil))

func (TB) InDelta added in v1.13.0

func (t TB) InDelta(actual, expected, delta any, msg ...any) bool

InDelta checks for expected-delta <= actual <= expected+delta.

All three actual, expected and delta must be either:

func (TB) InSMAPE added in v1.13.0

func (t TB) InSMAPE(actual, expected any, smape float64, msg ...any) bool

InSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is less than given smape.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats

Allowed smape values are: 0.0 < smape < 100.0.

Used formula returns SMAPE value between 0 and 100 (percents):

  • 0.0 when actual == expected
  • ~0.5 when they differs in ~1%
  • ~5 when they differs in ~10%
  • ~20 when they differs in 1.5 times
  • ~33 when they differs in 2 times
  • 50.0 when they differs in 3 times
  • ~82 when they differs in 10 times
  • 99.0+ when actual and expected differs in 200+ times
  • 100.0 when only one of actual or expected is 0 or one of them is positive while another is negative

func (TB) JSONEqual added in v1.13.0

func (t TB) JSONEqual(actual, expected any, msg ...any) bool

JSONEqual normalize formatting of actual and expected (if they're valid JSON) and then checks for bytes.Equal(actual, expected).

Both actual and expected may have any of these types:

In case any of actual or expected is nil or empty or (for string or []byte) is invalid JSON - check will fail.

func (TB) LE added in v1.13.0

func (t TB) LE(actual, expected any, msg ...any) bool

LE is a synonym for LessOrEqual.

func (TB) LT added in v1.13.0

func (t TB) LT(actual, expected any, msg ...any) bool

LT is a synonym for Less.

func (TB) Len added in v1.13.0

func (t TB) Len(actual any, expected int, msg ...any) bool

Len checks is len(actual) == expected.

func (TB) Less added in v1.13.0

func (t TB) Less(actual, expected any, msg ...any) bool

Less checks for actual < expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) LessOrEqual added in v1.13.0

func (t TB) LessOrEqual(actual, expected any, msg ...any) bool

LessOrEqual checks for actual <= expected.

Both actual and expected must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) Match added in v1.13.0

func (t TB) Match(actual, regex any, msg ...any) bool

Match checks for regex.MatchString(actual).

Regex type can be either *regexp.Regexp or string.

Actual type can be:

  • string - will match with actual
  • []byte - will match with string(actual)
  • []rune - will match with string(actual)
  • fmt.Stringer - will match with actual.String()
  • error - will match with actual.Error()
  • nil - will not match (even with empty regex)

func (*TB) MergeContext added in v1.13.0

func (t *TB) MergeContext(ctx context.Context) *TB

MergeContext returns a derived *TB whose Context() combines ctx with the current Context(): values are looked up in ctx first, falling back to the current Context(); cancellation/deadline come from both, whichever happens first. Calling MergeContext again merges in one more context.

This is meant for injecting an application base context (e.g. one carrying a slog handler) into tests, on top of the per-test cancellation/deadline testing.TB.Context() already provides.

Example

ExampleTB_MergeContext injects an application base context (e.g. one carrying a slog handler) into a test on top of the per-test cancellation/deadline testing.TB.Context already provides.

package main

import (
	"context"
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	t := check.Must(tt)

	type slogHandlerKey struct{}
	appCtx := context.WithValue(context.Background(), slogHandlerKey{}, "app-handler")

	t = t.MergeContext(appCtx)
	t.NotNil(t.Context().Value(slogHandlerKey{}))
}

func (TB) Must added in v1.13.0

func (c TB) Must(continueTest bool, msg ...any)

Must interrupt test using t.FailNow if called with false value.

This provides an easy way to turn any check into assertion:

t.Must(t.Nil(err))

func (*TB) MustAll added in v1.13.0

func (t *TB) MustAll() *TB

MustAll creates and returns new *TB, which have only one difference from original one: every failed check will interrupt test using t.FailNow. You can continue using both old and new *TB at same time.

This provides an easy way to turn all checks into assertion.

func (TB) NE added in v1.13.0

func (t TB) NE(actual, expected any, msg ...any) bool

NE is a synonym for NotEqual.

func (TB) Nil added in v1.13.0

func (t TB) Nil(actual any, msg ...any) bool

Nil checks for actual == nil.

There is one subtle difference between this check and Go `== nil` (if this surprises you then you should read https://golang.org/doc/faq#nil_error first):

var intPtr *int
var empty interface{}
var notEmpty interface{} = intPtr
t.True(intPtr == nil)   // TRUE
t.True(empty == nil)    // TRUE
t.True(notEmpty == nil) // FALSE

When you call this function your actual value will be stored in interface{} argument, and this makes any typed nil pointer value `!= nil` inside this function (just like in example above happens with notEmpty variable).

As it is very common case to check some typed pointer using Nil this check has to work around and detect nil even if usual `== nil` return false. But this has nasty side effect: if actual value already was of interface type and contains some typed nil pointer (which is usually bad thing and should be avoid) then Nil check will pass (which may be not what you want/expect):

t.Nil(nil)              // TRUE
t.Nil(intPtr)           // TRUE
t.Nil(empty)            // TRUE
t.Nil(notEmpty)         // WARNING: also TRUE!

Second subtle case is less usual: uintptr(0) is sorta nil, but not really, so Nil(uintptr(0)) will fail. Nil(unsafe.Pointer(nil)) will also fail, for the same reason. Please do not use this and consider this behaviour undefined, because it may change in the future.

func (TB) NotBetween added in v1.13.0

func (t TB) NotBetween(actual, minimum, maximum any, msg ...any) bool

NotBetween checks for actual <= min or max <= actual.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) NotBetweenOrEqual added in v1.13.0

func (t TB) NotBetweenOrEqual(actual, minimum, maximum any, msg ...any) bool

NotBetweenOrEqual checks for actual < min or max < actual.

All three actual, min and max must be either:

  • signed integers
  • unsigned integers
  • floats
  • strings
  • time.Time

func (TB) NotBytesEqual added in v1.13.0

func (t TB) NotBytesEqual(actual, expected []byte, msg ...any) bool

NotBytesEqual checks for !bytes.Equal(actual, expected).

Hint: NotBytesEqual([]byte{}, []byte(nil)) is false (unlike NotDeepEqual).

func (TB) NotContains added in v1.13.0

func (t TB) NotContains(actual, expected any, msg ...any) bool

NotContains checks is actual not contains substring/element expected.

See Contains about supported actual/expected types and check logic.

func (TB) NotDeepEqual added in v1.13.0

func (t TB) NotDeepEqual(actual, expected any, msg ...any) bool

NotDeepEqual checks for !deepequal.DeepEqual(actual, expected). It will use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.).

Custom equal checkers registered via RegisterEqualChecker run first.

func (TB) NotDirExists added in v1.13.0

func (t TB) NotDirExists(path string, msg ...any) bool

NotDirExists checks that path does not exist or is not a directory.

See FileExists about Stat error handling.

func (TB) NotEqual added in v1.13.0

func (t TB) NotEqual(actual, expected any, msg ...any) bool

NotEqual checks for actual != expected.

func (TB) NotErr added in v1.13.0

func (t TB) NotErr(actual, expected error, msg ...any) bool

NotErr checks is actual error is not the same as expected error.

It tries to recursively unwrap actual before checking using errors.Unwrap() and github.com/pkg/errors.Cause(). In case of multi-error (Unwrap() []error) it use only first error.

They must have either different types or values (or one should be nil). Different instances with same type and value will be considered the same error, and so is both nil.

Finally it'll use !errors.Is().

func (TB) NotErrAs added in v1.13.0

func (t TB) NotErrAs(actual error, target any, msg ...any) bool

NotErrAs checks for !errors.As.

target must be a non-nil pointer to an error type or to an interface, as required by errors.As. Note that errors.As may still fill target with a matched error even when this check returns true, because errors.As is always called regardless of the negated result.

func (TB) NotErrIs added in v1.13.0

func (t TB) NotErrIs(actual, expected error, msg ...any) bool

NotErrIs checks for !errors.Is().

See ErrIs for details. Note that nil is not matched by errors.Is against any non-nil error, so NotErrIs(nil, io.EOF) passes.

func (TB) NotFileExists added in v1.13.0

func (t TB) NotFileExists(path string, msg ...any) bool

NotFileExists checks that path does not exist or is a directory.

See FileExists about Stat error handling.

func (TB) NotHasKey added in v1.13.0

func (t TB) NotHasKey(actual, expected any, msg ...any) bool

NotHasKey checks is actual has no key expected.

func (TB) NotHasPrefix added in v1.13.0

func (t TB) NotHasPrefix(actual, expected any, msg ...any) bool

NotHasPrefix checks for !strings.HasPrefix(actual, expected).

See HasPrefix about supported actual/expected types and check logic.

func (TB) NotHasSuffix added in v1.13.0

func (t TB) NotHasSuffix(actual, expected any, msg ...any) bool

NotHasSuffix checks for !strings.HasSuffix(actual, expected).

See HasSuffix about supported actual/expected types and check logic.

func (TB) NotHasType added in v1.13.0

func (t TB) NotHasType(actual, expected any, msg ...any) bool

NotHasType checks is actual has not same type as expected.

func (TB) NotImplements added in v1.13.0

func (t TB) NotImplements(actual, expected any, msg ...any) bool

NotImplements checks is actual does not implements interface pointed by expected.

You must use pointer to interface type in expected:

t.NotImplements(os.Stdin, (*fmt.Stringer)(nil))

func (TB) NotInDelta added in v1.13.0

func (t TB) NotInDelta(actual, expected, delta any, msg ...any) bool

NotInDelta checks for actual < expected-delta or expected+delta < actual.

All three actual, expected and delta must be either:

func (TB) NotInSMAPE added in v1.13.0

func (t TB) NotInSMAPE(actual, expected any, smape float64, msg ...any) bool

NotInSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is greater than or equal to given smape.

See InSMAPE about supported actual/expected types and check logic.

func (TB) NotLen added in v1.13.0

func (t TB) NotLen(actual any, expected int, msg ...any) bool

NotLen checks is len(actual) != expected.

func (TB) NotMatch added in v1.13.0

func (t TB) NotMatch(actual, regex any, msg ...any) bool

NotMatch checks for !regex.MatchString(actual).

See Match about supported actual/regex types and check logic.

func (TB) NotNil added in v1.13.0

func (t TB) NotNil(actual any, msg ...any) bool

NotNil checks for actual != nil.

See Nil about subtle case in check logic.

func (TB) NotPanic added in v1.13.0

func (t TB) NotPanic(actual func(), msg ...any) bool

NotPanic checks is actual() don't panics.

It is able to detect panic(nil)… but you should try to avoid using this.

func (TB) NotSortEqual added in v1.13.0

func (t TB) NotSortEqual(actual, expected any, msg ...any) bool

NotSortEqual checks !SortEqual(actual, expected).

See SortEqual about supported actual/expected types and check logic.

func (TB) NotSubset added in v1.13.0

func (t TB) NotSubset(actual, expected any, msg ...any) bool

NotSubset checks !Subset(actual, expected).

See Subset about supported actual/expected types and check logic.

func (TB) NotZero added in v1.13.0

func (t TB) NotZero(actual any, msg ...any) bool

NotZero checks is actual is not zero value of it's type.

func (TB) Panic added in v1.13.0

func (t TB) Panic(actual func(), msg ...any) bool

Panic checks is actual() panics.

It is able to detect panic(nil)… but you should try to avoid using this.

func (TB) PanicMatch added in v1.13.0

func (t TB) PanicMatch(actual func(), regex any, msg ...any) bool

PanicMatch checks is actual() panics and panic text match regex.

Regex type can be either *regexp.Regexp or string.

In case of panic(nil) it will match like panic("<nil>").

func (TB) PanicNotMatch added in v1.13.0

func (t TB) PanicNotMatch(actual func(), regex any, msg ...any) bool

PanicNotMatch checks is actual() panics and panic text not match regex.

Regex type can be either *regexp.Regexp or string.

In case of panic(nil) it will match like panic("<nil>").

func (*TB) Should added in v1.13.0

func (t *TB) Should(anyShouldFunc any, args ...any) bool

Should use user-provided check function to do actual check.

anyShouldFunc must have type ShouldFunc1 or ShouldFunc2. It should return true if check was successful. There is no need to call t.Error in anyShouldFunc - this will be done automatically when it returns.

args must contain at least 1 element for ShouldFunc1 and at least 2 elements for ShouldFunc2. Rest of elements will be processed as usual msg ...interface{} param.

Example:

func bePositive(_ *check.TB, actual interface{}) bool {
	return actual.(int) > 0
}
func TestCustomCheck(tt *testing.T) {
	t := check.T(tt)
	t.Should(bePositive, 42, "custom check!!!")
}
Example

ExampleTB_Should plugs a custom checker (bePositive, defined in check_test.go) into check's usual report/Must/TODO machinery, for checks not covered by any built-in checker.

tt := new(testing.T)
t := check.Must(tt)

t.Should(bePositive, 42, "custom check")

func (TB) SortEqual added in v1.13.0

func (t TB) SortEqual(actual, expected any, msg ...any) bool

SortEqual checks that actual and expected contain the same elements, ignoring order (multiset equality, duplicates counted).

Both actual and expected must be slices or arrays. Elements need not be sortable and are compared like DeepEqual. Nil and empty slices are equal (like BytesEqual, unlike DeepEqual).

Example

ExampleTB_SortEqual checks that two slices/arrays contain the same elements while ignoring their order.

package main

import (
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	t := check.Must(tt)

	t.SortEqual([]int{1, 2, 3}, []int{3, 1, 2})
}

func (TB) Subset added in v1.13.0

func (t TB) Subset(actual, expected any, msg ...any) bool

Subset checks that actual contains all elements of expected: for slices/arrays - as multisets (duplicates counted), ignoring order; for maps - every key of expected exists in actual with an equal value.

actual and expected must both be slices/arrays or both be maps. Elements/values are compared like DeepEqual. An empty/nil expected is a subset of anything of the same kind.

Note: unlike testify's Subset, duplicates are counted, so [1,1] is not a subset of [1].

func (*TB) TODO added in v1.13.0

func (t *TB) TODO() *TB

TODO creates and returns new *TB, which have only one difference from original one: every passing check is now handled as failed and vice versa (this doesn't affect boolean value returned by check). You can continue using both old and new *TB at same time.

Swapping passed/failed gives you ability to temporary mark some failed test as passed. For example, this may be useful to avoid broken builds in CI. This is often better than commenting, deleting or skipping broken test because it will continue to execute, and eventually when reason why it fails will be fixed this test will became failed again - notifying you the mark can and should be removed from this test now.

Example

ExampleTB_TODO marks a known-broken check as expected-to-fail without disabling or deleting the test: it keeps running, and once the underlying defect is fixed this check starts failing again - a reminder to remove TODO.

package main

import (
	"testing"

	"github.com/powerman/check"
)

func main() {
	tt := new(testing.T)
	t := check.Must(tt)

	t.TODO().Equal(2+2, 5)
}

func (TB) True added in v1.13.0

func (t TB) True(cond bool, msg ...any) bool

True checks for cond == true.

This can be useful to use your own custom checks, but this way you won't get nice dump/diff for actual/expected values. You'll still have statistics about passed/failed checks and it's shorter than usual:

if !cond {
	t.Errorf(msg...)
}

func (TB) Zero added in v1.13.0

func (t TB) Zero(actual any, msg ...any) bool

Zero checks is actual is zero value of it's type.

Directories

Path Synopsis
internal
contextx
Package contextx merges two context.Context values into one that looks up values in both and is cancelled when either one is.
Package contextx merges two context.Context values into one that looks up values in both and is cancelled when either one is.
deepequal
Package deepequal provides improved reflect.DeepEqual.
Package deepequal provides improved reflect.DeepEqual.
difflib
Package difflib is a partial port of Python difflib module.
Package difflib is a partial port of Python difflib module.
spew
Package spew implements a deep pretty printer for Go data structures to aid in debugging.
Package spew implements a deep pretty printer for Go data structures to aid in debugging.

Jump to

Keyboard shortcuts

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