Categorygithub.com/fluentassert/verify
modulepackage
1.2.0
Repository: https://github.com/fluentassert/verify.git
Documentation: pkg.go.dev

# README

fluentassert

Extensible, type-safe, fluent assertion Go library.

Go Reference Keep a Changelog GitHub Release go.mod LICENSE

Build Status Go Report Card Codecov Mentioned in Awesome Go

Please ⭐ Star this repository if you find it valuable and worth maintaining.

Description

The fluent API makes the assertion code easier to read and write (more).

The generics (type parameters) make the usage type-safe.

The library is extensible by design.

In general, avoid using assertion libraries. Use this library if you still have a preference to use one. Consider using github.com/google/go-cmp and writing custom helpers instead.

Quick start

package test

import (
	"testing"

	"github.com/fluentassert/verify"
)

func Foo() (string, error) {
	return "wrong", nil
}

func TestFoo(t *testing.T) {
	got, err := Foo()

	verify.NoError(err).Require(t)           // Require(f) uses t.Fatal(f), stops execution if fails
	verify.String(got).Equal("ok").Assert(t) // Assert(f) uses t.Error(f), continues execution if fails
}
$ go test
--- FAIL: TestFoo (0.00s)
    basic_test.go:17:
        the objects are not equal
        got: "wrong"
        want: "ok"

⚠ Do not forget calling Assert(t) or Require(t) which executes the actual assertion.

Supported types

Out-of-the-box the package provides fluent assertions for the following types. The more specific function you use, the more assertions you get.

Go typeAssertion entry point
interface{} (any)verify.Any()
comparableverify.Obj()
constraints.Orderedverify.Ordered()
constraints.Numberverify.Number()
stringverify.String()
errorverify.Error()
[]T (slice)verify.Slice()
map[K]V (map)verify.Map()

Below you can find some convenience functions.

Deep equality

For testing deep equality use DeepEqual() or NotDeepEqual().

package test

import (
	"testing"

	"github.com/fluentassert/verify"
)

type A struct {
	Str   string
	Bool  bool
	Slice []int
}

func TestDeepEqual(t *testing.T) {
	got := A{Str: "wrong", Slice: []int{1, 4}}

	verify.Any(got).DeepEqual(
		A{Str: "string", Bool: true, Slice: []int{1, 2}},
	).Assert(t)
}
$ go test
--- FAIL: TestDeepEqual (0.00s)
    deepeq_test.go:20:
        mismatch (-want +got):
          test.A{
        -       Str:  "string",
        +       Str:  "wrong",
        -       Bool: true,
        +       Bool: false,
                Slice: []int{
                        1,
        -               2,
        +               4,
                },
          }

Collection assertions

The library contains many collection assertion. Below is an example of checking unordered equality.

package test

import (
	"testing"

	"github.com/fluentassert/verify"
)

func TestSlice(t *testing.T) {
	got := []int { 3, 1, 2 }

	verify.Slice(got).Equivalent([]int { 2, 3, 4 }).Assert(t)
}
$ go test
--- FAIL: TestSlice (0.00s)
    slice_test.go:12:
        not equivalent
        got: [3 1 2]
        want: [2 3 4]
        extra got: [1]
        extra want: [4]

Periodic polling

For asynchronous testing you can use verify.Eventually() or verify.EventuallyChan().

package test

import (
	"net/http"
	"testing"
	"time"

	"github.com/fluentassert/verify"
)

func TestPeriodic(t *testing.T) {
	verify.Eventually(10*time.Second, time.Second, func() verify.FailureMessage {
		client := http.Client{Timeout: time.Second}
		resp, err := client.Get("http://not-existing:1234")
		if err != nil {
			return verify.NoError(err)
		}
		return verify.Number(resp.StatusCode).Lesser(300)
	}).Assert(t)
}
$ go test
--- FAIL: TestPeriodic (10.00s)
    async_test.go:19:
        function never passed, last failure message:
        Get "http://not-existing:1234": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

Custom predicates

For the most basic scenarios, you can use one of the Check(), Should(), ShouldNot() assertions.

package test

import (
	"strings"
	"testing"

	"github.com/fluentassert/verify"
)

func TestShould(t *testing.T) {
	got := "wrong"

	chars := "abc"
	verify.Any(got).Should(func(got string) bool {
		return strings.ContainsAny(got, chars)
	}).Assertf(t, "does not contain any of: %s", chars)
}
$ go test
--- FAIL: TestShould (0.00s)
    should_test.go:16: does not contain any of: abc
        object does not meet the predicate criteria
        got: "wrong"

Panics

For testing panics use verify.Panics() and verify.NotPanics().

Custom assertion function

You can create a function that returns FailureMessage. Use verify.And() and verify.Or() functions together with Prefix() method to create complex assertions.

package test

import (
	"testing"

	"github.com/fluentassert/verify"
)

type A struct {
	Str string
	Ok  bool
}

func TestCustom(t *testing.T) {
	got := A{Str: "something was wrong"}

	verifyA(got).Assert(t)
}

func verifyA(got A) verify.FailureMessage {
	return verify.And(
		verify.String(got.Str).Contain("ok").Prefix("got.String: "),
		verify.True(got.Ok).Prefix("got.Ok: "),
	)
}
$ go test
--- FAIL: TestCustom (0.00s)
    custom_test.go:17:
        got.String: the value does not contain the substring
        got: "something was wrong"
        substr: "ok"

        got.Ok: the value is false

Extensibility

You can take advantage of the FailureMessage and Fluent* types to create your own fluent assertions for a given type.

For reference, take a look at the implementation of existing fluent assertions in this repository (for example comparable.go).

Supported Go versions

Minimal supported Go version is 1.18.

Contributing

See CONTRIBUTING.md if you want to help.

License

fluentassert is licensed under the terms of the MIT license.

github.com/google/go-cmp (license: BSD-3-Clause) is the only third-party dependency.

# Functions

And accumalates non-empty failure messages.
Any is used for testing any object.
Error is used for testing error object.
Eventually executes the test function until it returns an empty FailureMessage or timeout elapses.
EventuallyChan executes the test function until it returns an empty FailureMessage or timeout elapses.
False tests if the object is a false value.
IsError tests if the error is non-nil.
Map is used for testing a map.
Nil tests if provided interface value is nil.
NoError tests if the error is nil.
NotNil tests if provided interface is not nil.
NotPanics tests if the function does not panic when executed.
Number is used for testing numbers that supports the operators < <= >= > + - * /.
Obj is used for testing a comparable object.
Or accumalates failure messages if all are not empty.
Ordered is used for testing a ordered object that supports the operators < <= >= >.
Panics tests if the function panics when executed.
Slice is used for testing a slice.
String is used for testing a string object.
True tests if the object is a true value.

# Structs

AssertionError is an error type used to represent failure messages from assertions.
FluentAny encapsulates assertions for any object.
FluentError encapsulates assertions for error object.
FluentMap encapsulates assertions for a map.
FluentNumber encapsulates assertions for numbers that supports the operators < <= >= > + - * /.
FluentObj encapsulates assertions for comparable object.
FluentOrdered encapsulates assertions for ordered object that supports the operators < <= >= >.
FluentSlice encapsulates assertions for a slice.
FluentString encapsulates assertions for string object.

# Type aliases

FailureMessage encapsulates a failure message that can by emitted using objects compatible with the testing.TB interface.