assert

package
v0.138.0 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2023 License: Apache-2.0 Imports: 16 Imported by: 0

README

testcase/assert

This package meant to provide a small and dependency lightweight implementation for common assertion related requirements.

Example:

assert.Should(tb).True(true)
assert.Must(tb).Equal(expected, actual, "error message")
assert.Must(tb).NotEqual(true, false, "exp")
assert.Must(tb).Contain([]int{1, 2, 3}, 3, "exp")
assert.Must(tb).Contain([]int{1, 2, 3}, []int{1, 2}, "exp")
assert.Must(tb).Contain(map[string]int{"The Answer": 42, "oth": 13}, map[string]int{"The Answer": 42}, "exp")

For more examples, check out the example_test.go file.

Documentation

Overview

Example (ConfigureDiffFunc)
package main

import (
	"fmt"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	assert.DiffFunc = func(value, othValue any) string {
		return fmt.Sprintf("%#v | %#v", value, othValue)
	}

	var tb testing.TB
	assert.Equal(tb, "foo", "bar")
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var DiffFunc diffFn = pp.DiffFormat[any]

DiffFunc is the function that will be used to print out two object if they are not equal. You can use your preferred diff implementation if you are not happy with the pretty print diff format.

Functions

func Contain added in v0.80.0

func Contain(tb testing.TB, haystack, needle any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Contain(tb, []int{1, 2, 3}, 3, "optional assertion explanation")
	assert.Must(tb).Contain(tb, []int{1, 2, 3}, []int{1, 2}, "optional assertion explanation")
	assert.Must(tb).Contain(tb,
		map[string]int{"The Answer": 42, "oth": 13},
		map[string]int{"The Answer": 42},
		"optional assertion explanation")
}
Output:

func ContainExactly added in v0.80.0

func ContainExactly[T any](tb testing.TB, expected, actual T, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.ContainExactly(tb, []int{1, 2, 3}, []int{2, 3, 1}, "optional assertion explanation")  // true
	assert.ContainExactly(tb, []int{1, 2, 3}, []int{1, 42, 2}, "optional assertion explanation") // false
}
Output:

func Empty added in v0.80.0

func Empty(tb testing.TB, v any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Empty(tb, "")       // ok
	assert.Empty(tb, "oh no!") // Fatal
}
Output:

func Equal added in v0.80.0

func Equal[T any](tb testing.TB, expected, actually T, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Equal(tb, "a", "a")
	assert.Equal(tb, 42, 42)
	assert.Equal(tb, []int{42}, []int{42})
	assert.Equal(tb, map[int]int{24: 42}, map[int]int{24: 42})
}
Output:

func Error added in v0.121.0

func Error(tb testing.TB, err error, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Error(tb, nil)                // fail
	assert.Error(tb, errors.New("boom")) // pass
}
Output:

func ErrorIs added in v0.80.0

func ErrorIs(tb testing.TB, expected, actual error, msg ...any)
Example
package main

import (
	"errors"
	"fmt"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	actualErr := errors.New("boom")
	assert.ErrorIs(tb, errors.New("boom"), actualErr)                                  // passes for equality
	assert.ErrorIs(tb, errors.New("boom"), fmt.Errorf("wrapped error: %w", actualErr)) // passes for wrapped errors
}
Output:

func False added in v0.80.0

func False(tb testing.TB, v bool, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.False(tb, false) // ok
	assert.False(tb, true)  // Fatal
}
Output:

func Nil added in v0.80.0

func Nil(tb testing.TB, v any, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Nil(tb, nil)                // ok
	assert.Nil(tb, errors.New("boom")) // Fatal
}
Output:

func NoError added in v0.94.0

func NoError(tb testing.TB, err error, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.NoError(tb, nil)                // pass
	assert.NoError(tb, errors.New("boom")) // fail
}
Output:

func NotContain added in v0.80.0

func NotContain(tb testing.TB, haystack, v any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).NotContain(tb, []int{1, 2, 3}, 42)
	assert.Must(tb).NotContain(tb, []int{1, 2, 3}, []int{1, 2, 42})
	assert.Must(tb).NotContain(tb,
		map[string]int{"The Answer": 42, "oth": 13},
		map[string]int{"The Answer": 41})
}
Output:

func NotEmpty added in v0.80.0

func NotEmpty(tb testing.TB, v any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.NotEmpty(tb, "huh...") // ok
	assert.NotEmpty(tb, "")       // Fatal
}
Output:

func NotEqual added in v0.80.0

func NotEqual[T any](tb testing.TB, expected, actually T, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.NotEqual(tb, "a", "b")
	assert.Equal(tb, 13, 42)
}
Output:

func NotNil added in v0.80.0

func NotNil(tb testing.TB, v any, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.NotNil(tb, errors.New("boom")) // ok
	assert.NotNil(tb, nil)                // Fatal
}
Output:

func NotPanic added in v0.80.0

func NotPanic(tb testing.TB, blk func(), msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.NotPanic(tb, func() {})                  // ok
	assert.NotPanic(tb, func() { panic("oh no!") }) // Fatal
}
Output:

func NotWithin added in v0.126.0

func NotWithin(tb testing.TB, timeout time.Duration, blk func(context.Context), msg ...any)
Example
package main

import (
	"context"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	assert.NotWithin(tb, time.Second, func(ctx context.Context) {
		return // FAIL
	})

	assert.NotWithin(tb, time.Nanosecond, func(ctx context.Context) {
		time.Sleep(time.Second) // OK
	})
}
Output:

func OneOf added in v0.132.0

func OneOf[V any](tb testing.TB, vs []V, blk func(it It, got V), msg ...any)

OneOf function checks a list of values and matches an expectation against each element of the list. If any of the elements pass the assertion, then the assertion helper function does not fail the test.

Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	values := []string{"foo", "bar", "baz"}

	assert.OneOf(tb, values, func(it assert.It, got string) {
		it.Must.Equal("bar", got)
	}, "optional assertion explanation")
}
Output:

func Panic added in v0.80.0

func Panic(tb testing.TB, blk func(), msg ...any) any
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	panicValue := assert.Panic(tb, func() { panic("at the disco") }) // ok
	assert.Equal(tb, "some expected panic value", panicValue)

	assert.Panic(tb, func() {}) // Fatal
}
Output:

func Read added in v0.97.0

func Read[T string | []byte](tb testing.TB, expected T, r io.Reader, msg ...any)
Example
package main

import (
	"strings"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Read(tb, "expected content", strings.NewReader("expected content"))  // pass
	assert.Read(tb, "expected content", strings.NewReader("different content")) // fail
}
Output:

func ReadAll added in v0.97.0

func ReadAll(tb testing.TB, r io.Reader, msg ...any) []byte
Example
package main

import (
	"errors"
	"strings"
	"testing"
	"testing/iotest"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	content := assert.ReadAll(tb, strings.NewReader("expected content")) // pass
	_ = content
	assert.ReadAll(tb, iotest.ErrReader(errors.New("boom"))) // fail
}
Output:

func Sub added in v0.122.0

func Sub[T any](tb testing.TB, haystack, needle []T, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Sub(tb, []int{1, 2, 3}, []int{1, 2}, "optional assertion explanation")
}
Output:

func True added in v0.80.0

func True(tb testing.TB, v bool, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.True(tb, true)  // ok
	assert.True(tb, false) // Fatal
}
Output:

func Within added in v0.124.0

func Within(tb testing.TB, timeout time.Duration, blk func(context.Context), msg ...any)
Example
package main

import (
	"context"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	assert.Within(tb, time.Second, func(ctx context.Context) {
		// OK
	})

	assert.Within(tb, time.Nanosecond, func(ctx context.Context) {
		time.Sleep(time.Second)
		// FAIL
	})
}
Output:

Types

type AnyOf added in v0.60.0

type AnyOf struct {
	TB   testing.TB
	Fail func()
	// contains filtered or unexported fields
}

AnyOf is an assertion helper that allows you run AnyOf.Test assertion blocks, that can fail, as lone at least one of them succeeds. common usage use-cases:

  • list of interface, where test order, or the underlying structure's implementation is irrelevant for the behavior.
  • list of big structures, where not all field value relevant, only a subset, like a structure it wraps under a field.
  • list of structures with fields that has dynamic state values, which is irrelevant for the given test.
  • structure that can have various state scenario, and you want to check all of them, and you expect to find one match with the input.
  • fan out scenario, where you need to check in parallel that at least one of the worker received the event.
Example (FanOutPublishing)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

type ExamplePublisherEvent struct{ V int }
type ExamplePublisher struct{}

func (ExamplePublisher) Publish(event ExamplePublisherEvent)         {}
func (ExamplePublisher) Subscribe(func(event ExamplePublisherEvent)) {}
func (ExamplePublisher) Wait()                                       {}
func (ExamplePublisher) Close() error                                { return nil }

func main() {
	var tb testing.TB
	publisher := ExamplePublisher{}
	anyOf := &assert.AnyOf{TB: tb, Fail: tb.FailNow}
	for i := 0; i < 42; i++ {
		publisher.Subscribe(func(event ExamplePublisherEvent) {
			anyOf.Test(func(it assert.It) {
				it.Must.Equal(42, event.V)
			})
		})
	}
	publisher.Publish(ExamplePublisherEvent{V: 42})
	publisher.Wait()
	assert.Must(tb).Nil(publisher.Close())
	anyOf.Finish()
}
Output:

Example (ListOfCompositedStructuresWhereOnlyTheEmbededValueIsRelevant)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	type BigStruct struct {
		ID            string // not relevant for the test
		A, B, C, D, E int    // not relevant data as well
		WrappedStruct struct {
			A, B, C int // relevant data for the test
		}
	}
	anyOf := assert.AnyOf{TB: tb, Fail: tb.FailNow}
	for _, v := range []BigStruct{} {
		anyOf.Test(func(it assert.It) {
			it.Must.Equal(42, v.WrappedStruct.A)
			it.Must.Equal(1, v.WrappedStruct.B)
			it.Must.Equal(2, v.WrappedStruct.C)
		})
	}
	anyOf.Finish()
}
Output:

Example (ListOfInterface)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	type ExampleInterface interface {
		Foo() int
		Bar() bool
		Baz() string
	}
	anyOf := assert.AnyOf{TB: tb, Fail: tb.FailNow}
	for _, v := range []ExampleInterface{} {
		anyOf.Test(func(it assert.It) {
			it.Must.True(v.Bar())
		})
	}
	anyOf.Finish()
}
Output:

Example (ListOfStructuresWithIrrelevantValues)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	type StructWithDynamicValues struct {
		IrrelevantStateValue int // not relevant data for the test
		ImportantValue       int
	}
	anyOf := assert.AnyOf{TB: tb, Fail: tb.FailNow}
	for _, v := range []StructWithDynamicValues{} {
		anyOf.Test(func(it assert.It) {
			it.Must.Equal(42, v.ImportantValue)
		})
	}
	anyOf.Finish()
}
Output:

Example (StructWithManyAcceptableState)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	type ExampleStruct struct {
		Type    string
		A, B, C int
	}
	var es ExampleStruct
	anyOf := assert.AnyOf{TB: tb, Fail: tb.FailNow}
	anyOf.Test(func(it assert.It) {
		it.Must.Equal(`foo`, es.Type)
		it.Must.Equal(1, es.A)
		it.Must.Equal(2, es.B)
		it.Must.Equal(3, es.C)
	})
	anyOf.Test(func(it assert.It) {
		it.Must.Equal(`foo`, es.Type)
		it.Must.Equal(3, es.A)
		it.Must.Equal(2, es.B)
		it.Must.Equal(1, es.C)
	})
	anyOf.Test(func(it assert.It) {
		it.Must.Equal(`bar`, es.Type)
		it.Must.Equal(11, es.A)
		it.Must.Equal(12, es.B)
		it.Must.Equal(13, es.C)
	})
	anyOf.Test(func(it assert.It) {
		it.Must.Equal(`baz`, es.Type)
		it.Must.Equal(21, es.A)
		it.Must.Equal(22, es.B)
		it.Must.Equal(23, es.C)
	})
	anyOf.Finish()
}
Output:

func (*AnyOf) Finish added in v0.60.0

func (ao *AnyOf) Finish(msg ...interface{})

Finish will check if any of the assertion succeeded.

func (*AnyOf) OK added in v0.132.0

func (ao *AnyOf) OK() bool

func (*AnyOf) Test added in v0.60.0

func (ao *AnyOf) Test(blk func(t It))

Test will test a block of assertion that must succeed in order to make AnyOf pass. You can have as much AnyOf.Test calls as you need, but if any of them pass with success, the rest will be skipped. Using Test is safe for concurrently.

type Asserter

type Asserter struct {
	TB   testing.TB
	Fail func()
}

func Must

func Must(tb testing.TB) Asserter
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	// create an assertion helper which will fail the testing context with .Fatal(...) in case of a failed assert.
	assert.Must(tb).True(true)
}
Output:

func Should

func Should(tb testing.TB) Asserter
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	// create an assertion helper which will fail the testing context with .Error(...) in case of a failed assert.
	assert.Should(tb).True(true)
}
Output:

func (Asserter) AnyOf added in v0.60.0

func (a Asserter) AnyOf(blk func(a *AnyOf), msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	var list []interface {
		Foo() int
		Bar() bool
		Baz() string
	}
	assert.Must(tb).AnyOf(func(anyOf *assert.AnyOf) {
		for _, testingCase := range list {
			anyOf.Test(func(it assert.It) {
				it.Must.True(testingCase.Bar())
			})
		}
	})
}
Output:

func (Asserter) Contain

func (a Asserter) Contain(haystack, needle any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Contain([]int{1, 2, 3}, 3, "optional assertion explanation")
	assert.Must(tb).Contain([]int{1, 2, 3}, []int{1, 2}, "optional assertion explanation")
	assert.Must(tb).Contain(map[string]int{"The Answer": 42, "oth": 13}, map[string]int{"The Answer": 42}, "optional assertion explanation")
}
Output:

func (Asserter) ContainExactly

func (a Asserter) ContainExactly(expected, actual any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).ContainExactly([]int{1, 2, 3}, []int{2, 3, 1}, "optional assertion explanation")  // true
	assert.Must(tb).ContainExactly([]int{1, 2, 3}, []int{1, 42, 2}, "optional assertion explanation") // false
}
Output:

func (Asserter) Empty added in v0.61.0

func (a Asserter) Empty(v any, msg ...any)

Empty gets whether the specified value is considered empty.

Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	assert.Must(tb).Empty([]int{})   // pass
	assert.Must(tb).Empty([]int{42}) // fail

	assert.Must(tb).Empty([42]int{})   // pass
	assert.Must(tb).Empty([42]int{42}) // fail

	assert.Must(tb).Empty(map[int]int{})       // pass
	assert.Must(tb).Empty(map[int]int{42: 24}) // fail

	assert.Must(tb).Empty("")   // pass
	assert.Must(tb).Empty("42") // fail
}
Output:

func (Asserter) Equal

func (a Asserter) Equal(expected, actually any, msg ...any)

Equal allows you to match if two entity is equal.

if entities are implementing IsEqual/Equal function, then it will be used to check equality between each other.

  • value.IsEqual(oth T) bool
  • value.IsEqual(oth T) (bool, error)
  • value.Equal(oth T) bool
  • value.Equal(oth T) (bool, error)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Equal(true, true, "optional assertion explanation")
}
Output:

Example (IsEqualFunctionThatSupportsErrorReturning)
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

type ExampleEqualableWithError struct {
	IrrelevantExportedField int
	relevantUnexportedValue int
	IsEqualErr              error
}

func (es ExampleEqualableWithError) IsEqual(oth ExampleEqualableWithError) (bool, error) {
	return es.relevantUnexportedValue == oth.relevantUnexportedValue, es.IsEqualErr
}

func main() {
	var tb testing.TB

	expected := ExampleEqualableWithError{
		IrrelevantExportedField: 42,
		relevantUnexportedValue: 24,
		IsEqualErr:              errors.New("sadly something went wrong"),
	}

	actual := ExampleEqualableWithError{
		IrrelevantExportedField: 42,
		relevantUnexportedValue: 24,
	}

	assert.Must(tb).Equal(expected, actual) // fails because the error returned from the IsEqual function.
}
Output:

Example (WithEqualMethod)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

type ExampleEqualableWithEqual struct {
	IrrelevantExportedField int
	relevantUnexportedValue int
}

func (es ExampleEqualableWithEqual) IsEqual(oth ExampleEqualableWithEqual) bool {
	return es.relevantUnexportedValue == oth.relevantUnexportedValue
}

func main() {
	var tb testing.TB

	expected := ExampleEqualableWithEqual{
		IrrelevantExportedField: 42,
		relevantUnexportedValue: 24,
	}

	actual := ExampleEqualableWithEqual{
		IrrelevantExportedField: 4242,
		relevantUnexportedValue: 24,
	}

	assert.Must(tb).Equal(expected, actual) // passes as by IsEqual terms the two value is equal
}
Output:

Example (WithIsEqualMethod)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

type ExampleEqualableWithIsEqual struct {
	IrrelevantExportedField int
	relevantUnexportedValue int
}

func (es ExampleEqualableWithIsEqual) IsEqual(oth ExampleEqualableWithIsEqual) bool {
	return es.relevantUnexportedValue == oth.relevantUnexportedValue
}

func main() {
	var tb testing.TB

	expected := ExampleEqualableWithIsEqual{
		IrrelevantExportedField: 42,
		relevantUnexportedValue: 24,
	}

	actual := ExampleEqualableWithIsEqual{
		IrrelevantExportedField: 4242,
		relevantUnexportedValue: 24,
	}

	assert.Must(tb).Equal(expected, actual) // passes as by IsEqual terms the two value is equal
}
Output:

func (Asserter) Error added in v0.120.0

func (a Asserter) Error(err error, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	asserter := assert.Should(tb)
	asserter.Error(nil)                // fail
	asserter.Error(errors.New("boom")) // pass
}
Output:

func (Asserter) ErrorIs added in v0.68.0

func (a Asserter) ErrorIs(expected, actual error, msg ...any)

ErrorIs allow you to assert an error value by an expectation. ErrorIs allow asserting an error regardless if it's wrapped or not. Suppose the implementation of the test subject later changes by wrap errors to add more context to the return error.

Example
package main

import (
	"errors"
	"fmt"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	actualErr := errors.New("boom")
	assert.Must(tb).ErrorIs(errors.New("boom"), actualErr)                                  // passes for equality
	assert.Must(tb).ErrorIs(errors.New("boom"), fmt.Errorf("wrapped error: %w", actualErr)) // passes for wrapped errors
}
Output:

func (Asserter) False added in v0.61.0

func (a Asserter) False(v bool, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).False(false, "optional assertion explanation")
}
Output:

func (Asserter) Nil

func (a Asserter) Nil(v any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Nil(nil, "optional assertion explanation")
}
Output:

func (Asserter) NoError added in v0.94.0

func (a Asserter) NoError(err error, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	asserter := assert.Should(tb)
	asserter.NoError(nil)                // pass
	asserter.NoError(errors.New("boom")) // fail
}
Output:

func (Asserter) NotContain

func (a Asserter) NotContain(haystack, v any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).NotContain([]int{1, 2, 3}, 42, "optional assertion explanation")
	assert.Must(tb).NotContain([]int{1, 2, 3}, []int{42}, "optional assertion explanation")
	assert.Must(tb).NotContain(map[string]int{"The Answer": 42, "oth": 13}, map[string]int{"The Answer": 13}, "optional assertion explanation")
}
Output:

func (Asserter) NotEmpty added in v0.61.0

func (a Asserter) NotEmpty(v any, msg ...any)

NotEmpty gets whether the specified value is considered empty.

Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB

	assert.Must(tb).NotEmpty([]int{42}, "optional assertion explanation")

	assert.Must(tb).NotEmpty([]int{})   // fail
	assert.Must(tb).NotEmpty([]int{42}) // pass

	assert.Must(tb).NotEmpty([42]int{})   // fail
	assert.Must(tb).NotEmpty([42]int{42}) // pass

	assert.Must(tb).NotEmpty(map[int]int{})       // fail
	assert.Must(tb).NotEmpty(map[int]int{42: 24}) // pass

	assert.Must(tb).NotEmpty("")   // fail
	assert.Must(tb).NotEmpty("42") // pass
}
Output:

func (Asserter) NotEqual added in v0.58.0

func (a Asserter) NotEqual(v, oth any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).NotEqual(true, false, "optional assertion explanation")
}
Output:

func (Asserter) NotNil

func (a Asserter) NotNil(v any, msg ...any)
Example
package main

import (
	"errors"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).NotNil(errors.New("42"), "optional assertion explanation")
}
Output:

func (Asserter) NotPanic

func (a Asserter) NotPanic(blk func(), msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).NotPanic(func() { /* no boom */ }, "optional assertion explanation")
}
Output:

func (Asserter) NotWithin added in v0.126.0

func (a Asserter) NotWithin(timeout time.Duration, blk func(context.Context), msg ...any)
Example
package main

import (
	"context"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	a := assert.Must(tb)

	a.NotWithin(time.Second, func(ctx context.Context) {
		return // FAIL
	})

	a.NotWithin(time.Nanosecond, func(ctx context.Context) {
		time.Sleep(time.Second) // OK
	})
}
Output:

func (Asserter) Panic

func (a Asserter) Panic(blk func(), msg ...any) any
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Panic(func() { panic("boom") }, "optional assertion explanation")
}
Output:

func (Asserter) Read added in v0.97.0

func (a Asserter) Read(expected any, r io.Reader, msg ...any)
Example
package main

import (
	"strings"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	must := assert.Must(tb)
	must.Read("expected content", strings.NewReader("expected content"))  // pass
	must.Read("expected content", strings.NewReader("different content")) // fail
}
Output:

func (Asserter) ReadAll added in v0.97.0

func (a Asserter) ReadAll(r io.Reader, msg ...any) []byte
Example
package main

import (
	"errors"
	"strings"
	"testing"
	"testing/iotest"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	must := assert.Must(tb)
	content := must.ReadAll(strings.NewReader("expected content")) // pass
	_ = content
	must.ReadAll(iotest.ErrReader(errors.New("boom"))) // fail
}
Output:

func (Asserter) Sub added in v0.122.0

func (a Asserter) Sub(slice, sub any, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).Sub([]int{1, 2, 3}, 3, "optional assertion explanation")
	assert.Must(tb).Sub([]int{1, 2, 3}, []int{1, 2}, "optional assertion explanation")
}
Output:

func (Asserter) True

func (a Asserter) True(v bool, msg ...any)
Example
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.Must(tb).True(true, "optional assertion explanation")
}
Output:

func (Asserter) Within added in v0.124.0

func (a Asserter) Within(timeout time.Duration, blk func(context.Context), msg ...any)
Example
package main

import (
	"context"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	a := assert.Must(tb)

	a.Within(time.Second, func(ctx context.Context) {
		// OK
	})

	a.Within(time.Nanosecond, func(ctx context.Context) {
		time.Sleep(time.Second)
		// FAIL
	})
}
Output:

type Eventually added in v0.84.0

type Eventually struct{ RetryStrategy RetryStrategy }

Eventually Automatically retries operations whose failure is expected under certain defined conditions. This pattern enables fault-tolerance.

A common scenario where using Eventually will benefit you is testing concurrent operations. Due to the nature of async operations, one might need to wait and observe the system with multiple tries before the outcome can be seen.

Example
package main

import (
	"math/rand"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	waiter := assert.Waiter{
		WaitDuration: time.Millisecond,
		Timeout:      time.Second,
	}
	w := assert.Eventually{RetryStrategy: waiter}

	var t *testing.T
	// will attempt to wait until assertion block passes without a failing testCase result.
	// The maximum time it is willing to wait is equal to the wait timeout duration.
	// If the wait timeout reached, and there was no passing assertion run,
	// the last failed assertion history is replied to the received testing.TB
	//   In this case the failure would be replied to the *testing.T.
	w.Assert(t, func(it assert.It) {
		if rand.Intn(1) == 0 {
			it.Fatal(`boom`)
		}
	})
}
Output:

Example (AsContextOption)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase"
	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	s := testcase.NewSpec(tb)

	s.Test(`flaky`, func(t *testcase.T) {
		// flaky test content here
	}, testcase.Flaky(assert.RetryCount(42)))
}
Output:

Example (ByCount)
package main

import (
	"math/rand"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	r := assert.Eventually{RetryStrategy: assert.RetryCount(42)}

	var t *testing.T
	r.Assert(t, func(it assert.It) {
		if rand.Intn(1) == 0 {
			it.Fatal(`boom`)
		}
	})
}
Output:

Example (ByCustomRetryStrategy)
package main

import (
	"math/rand"
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	// this approach ideal if you need to deal with asynchronous systems
	// where you know that if a workflow process ended already,
	// there is no point in retrying anymore the assertion.

	while := func(isFailed func() bool) {
		for isFailed() {
			// just retry while assertion is failed
			// could be that assertion will be failed forever.
			// Make sure the assertion is not stuck in a infinite loop.
		}
	}

	r := assert.Eventually{RetryStrategy: assert.RetryStrategyFunc(while)}

	var t *testing.T
	r.Assert(t, func(it assert.It) {
		if rand.Intn(1) == 0 {
			it.Fatal(`boom`)
		}
	})
}
Output:

Example (ByTimeout)
package main

import (
	"math/rand"
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	r := assert.Eventually{RetryStrategy: assert.Waiter{
		WaitDuration: time.Millisecond,
		Timeout:      time.Second,
	}}

	var t *testing.T
	r.Assert(t, func(it assert.It) {
		if rand.Intn(1) == 0 {
			it.Fatal(`boom`)
		}
	})
}
Output:

Example (Count)
package main

import (
	"github.com/adamluzsi/testcase/assert"
)

func main() {
	_ = assert.Eventually{RetryStrategy: assert.RetryCount(42)}
}
Output:

func EventuallyWithin added in v0.102.0

func EventuallyWithin[T time.Duration | int](durationOrCount T) Eventually
Example
package main

import (
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.EventuallyWithin(5*time.Second).Assert(tb, func(it assert.It) {
		// use "it" as you would tb, but if the test fails with "it"
		// then the function block will be retried until the allowed time duration, which is one minute in this case.
	})
}
Output:

Example (ByCount)
package main

import (
	"testing"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.EventuallyWithin(3 /* times */).Assert(tb, func(it assert.It) {
		// use "it" as you would tb, but if the test fails with "it"
		// it will be retried 3 times as specified above as argument.
	})
}
Output:

Example (ByTimeout)
package main

import (
	"testing"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	var tb testing.TB
	assert.EventuallyWithin(time.Minute /* times */).Assert(tb, func(it assert.It) {
		// use "it" as you would tb, but if the test fails with "it"
		// then the function block will be retried until the allowed time duration, which is one minute in this case.
	})
}
Output:

func (Eventually) Assert added in v0.84.0

func (r Eventually) Assert(tb testing.TB, blk func(it It))

Assert will attempt to assert with the assertion function block multiple times until the expectations in the function body met. In case expectations are failed, it will retry the assertion block using the RetryStrategy. The last failed assertion results would be published to the received testing.TB. Calling multiple times the assertion function block content should be a safe and repeatable operation.

type It added in v0.60.0

type It struct {
	testing.TB
	// Must Asserter will use FailNow on a failed assertion.
	// This will make test exit early on.
	Must Asserter
	// Should Asserter's will allow to continue the test scenario,
	// but mark test failed on a failed assertion.
	Should Asserter
}

func MakeIt added in v0.63.0

func MakeIt(tb testing.TB) It

type RetryStrategy added in v0.84.0

type RetryStrategy interface {
	// While implements the retry strategy looping part.
	// Depending on the outcome of the condition,
	// the RetryStrategy can decide whether further iterations can be done or not
	While(condition func() bool)
}

func RetryCount added in v0.84.0

func RetryCount(times int) RetryStrategy

type RetryStrategyFunc added in v0.84.0

type RetryStrategyFunc func(condition func() bool)

func (RetryStrategyFunc) While added in v0.84.0

func (fn RetryStrategyFunc) While(condition func() bool)

type Waiter added in v0.84.0

type Waiter struct {
	// WaitDuration is the time how lone Waiter.Wait should wait between attempting a new retry during Waiter.While.
	WaitDuration time.Duration
	// Timeout is used to calculate the deadline for the Waiter.While call.
	// If the retry takes longer than the Timeout, the retry will be cancelled.
	Timeout time.Duration
}

Waiter is a component that waits for a time, event, or opportunity.

func (Waiter) Wait added in v0.84.0

func (w Waiter) Wait()

Wait will attempt to wait a bit and leave breathing space for other goroutines to steal processing time. It will also attempt to schedule other goroutines.

Example
package main

import (
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	w := assert.Waiter{WaitDuration: time.Millisecond}

	w.Wait() // will wait 1 millisecond and attempt to schedule other go routines
}
Output:

func (Waiter) While added in v0.84.0

func (w Waiter) While(condition func() bool)

While will wait until a condition met, or until the wait timeout. By default, if the timeout is not defined, it just attempts to execute the condition once. Calling multiple times the condition function should be a safe operation.

Example
package main

import (
	"math/rand"
	"time"

	"github.com/adamluzsi/testcase/assert"
)

func main() {
	w := assert.Waiter{
		WaitDuration: time.Millisecond,
		Timeout:      time.Second,
	}

	// will attempt to wait until condition returns false.
	// The maximum time it is willing to wait is equal to the wait timeout duration.
	w.While(func() bool {
		return rand.Intn(1) == 0
	})
}
Output:

Jump to

Keyboard shortcuts

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