A modern generic testing assertions library for Go

test

GoDoc Go Report Card CI Tests License: MPL 2.0

test is a generics based testing assertions library for Go.

There are two packages, test and must.

  • test - assertions that mark the test for failure and allow the test case to continue
  • must - assertions that mark the test for failure and halt the test case immediately

Requirements

Only depends on github.com/google/go-cmp.

The minimum Go version is go1.18.

Install

Use go get to grab the latest version of test.

go get -u github.com/shoenig/test@latest

Influence

This library was made after a ~decade of using testify, quite possibly the most used library in the whole Go ecosystem. All credit of inspiration belongs them.

Philosophy

Go has always lacked a strong definition of equivalency, and until recently lacked the language features necessary to make type-safe yet generic assertive statements based on the contents of values.

This test (and companion must) package aims to provide a test-case assertion library where the caller is in control of how types are compared, and to do so in a strongly typed way - avoiding erroneous comparisons in the first place.

Generally there are 4 ways of asserting equivalence between types.

the == operator

Functions like EqOp and ContainsOp work on types that are comparable, i.e. are compatible with Go's built-in == and != operators.

a comparator function

Functions like EqFunc and ContainsFunc work on any type, as the caller passes in a function that takes two arguments of that type, returning a boolean indicating equivalence.

an .Equals method

Functions like Equals and ContainsEquals work on types implementing the EqualsFunc generic interface (i.e. implement an .Equals method). The .Equals method is called to determine equivalence.

the cmp.Equal or reflect.DeepEqual functions

Functions like Eq and Contains work on any type, using the cmp.Equal or reflect.DeepEqual functions to determine equivalence. Although this is the easiest / most compatible way to "just compare stuff", it the least deterministic way of comparing instances of a type. Changes to the underlying types may cause unexpected changes in their equivalence (e.g. the addition of unexported fields, function field types, etc.).

output

When possible, a nice diff output is created to show why an equivalence has failed. This is done via the cmp.Diff function. For incompatible types, their GoString values are printed instead.

All output is directed through t.Log functions, and is visible only if test verbosity is turned on (e.g. go test -v).

fail fast vs. fail later

The test and must packages are identical, except for how test cases behave when encountering a failure. Sometimes it is helpful for a test case to continue running even though a failure has occurred (e.g. contains cleanup logic not captured via a t.Cleanup function). Other times it make sense to fail immediately and stop the test case execution.

test - functions allow test cases to continue execution

must - functions stop test case execution immediately

Examples (equality)

import "github.com/shoenig/test/must"

// ... 

e1 := Employee{ID: 100, Name: "Alice"}
e2 := Employee{ID: 101, Name: "Bob"}

// using cmp.Equal (like magic!)
must.Eq(t, e1, e2)

// using == operator
must.EqOp(t, e1, e2)

// using a custom comparator
must.EqFunc(t, e1, e2, func(a, b *Employee) bool {
    return a.ID == b.ID
})

// using .Equals method
must.Equals(t, e1, e2)

Examples (slices)

import "github.com/shoenig/test/must"

// ... 

a := []*Employee{
  {ID: 100, Name: "Alice"},
  {ID: 101, Name: "Bob"},
  {ID: 102, Name: "Carl"},
}
b := []*Employee{
  {ID: 100, Name: "Alice"},
  {ID: 101, Name: "Bob"},
  {ID: 103, Name: "Dian"},
}

must.EqSliceFunc(tc, a, b, func(a, b *Person) bool {
  return a.ID == b.ID && a.Name == b.Name
})

Examples (maps)

import "github.com/shoenig/test/must"

// ... 

a := map[int]Person{
  0: {ID: 100, Name: "Alice"},
  1: {ID: 101, Name: "Bob"},
}

b := map[int]Person{
  0: {ID: 100, Name: "Alice"},
  1: {ID: 101, Name: "Bob B."},
}

must.MapEqFunc(tc, a, b, func(p1, p2 Person) bool {
  return p1.ID == p2.ID && p1.Name == p2.Name
})

Output

The test and must package attempt to create useful, readable output when an assertions goes awry. Some random examples below.

test_test.go:779: expected different file permissions
↪ name: find
↪ exp: -rw-rwx-wx
↪ got: -rwxr-xr-x
tests_test.go:569: expected maps of same values via 'eq' function
↪ difference:
  map[int]test.Person{
  	0: {ID: 100, Name: "Alice"},
  	1: {
  		ID:   101,
- 		Name: "Bob",
+ 		Name: "Bob B.",
  	},
  }
test_test.go:520: expected slice[1].Less(slice[2])
↪ slice[1]: &{200 Bob}
↪ slice[2]: &{150 Carl}
test_test.go:688: expected maps of same values via .Equals method
↪ differential ↷
  map[int]*test.Person{
  	0: &{ID: 100, Name: "Alice"},
  	1: &{
- 		ID:   101,
+ 		ID:   200,
  		Name: "Bob",
  	},
  }
test_test.go:801: expected regexp match
↪ s: abcX
↪ re: abc\d

License

Open source under the MPL

Comments
  • build(deps): bump github.com/google/go-cmp from 0.5.8 to 0.5.9

    build(deps): bump github.com/google/go-cmp from 0.5.8 to 0.5.9

    Bumps github.com/google/go-cmp from 0.5.8 to 0.5.9.

    Release notes

    Sourced from github.com/google/go-cmp's releases.

    v0.5.9

    Reporter changes:

    • (#299) Adjust heuristic for line-based versus byte-based diffing
    • (#306) Use value.TypeString in PathStep.String

    Code cleanup changes:

    • (#297) Use reflect.Value.IsZero
    • (#304) Format with Go 1.19 formatter
    • (#300 )Fix typo in Result documentation
    • (#302) Pre-declare global type variables
    • (#309) Run tests on Go 1.19
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • v0.5: fixup Contains parameters order

    v0.5: fixup Contains parameters order

    In a few cases like StrContains, the expectation comes after the value which is backwards from most other assertion styles. Would be nice to get these consistent.

  • idea: adding additional context to tests

    idea: adding additional context to tests

    One feature left off is support of the msgAndArgs ...any pattern from testify. Though used rarely, it does provide a convenient way of adding more context to test failures in complex scenarios like in e2e tests.

    Might try something like having each test function return an object (or func) and use that to optionally add more context, rather than the varargs.

  • idea: be able to assert a condition during a constraint

    idea: be able to assert a condition during a constraint

    Kind of the inverse of wait.On, something like wait.Until which will assert a given function returns a positive condition for the period of time / attempts specified

  • bug: SliceContainsFunc should be more flexible on types

    bug: SliceContainsFunc should be more flexible on types

    Currently SliceContainsFunc asserts the given array and expected object are of the same type T, and the comparison function is (a, b T) bool. This prevents us from using the comparison func to extract a different type to compare (i.e. a particular field of a struct).

  • idea: be able to set a cleanup function on a wait constraint

    idea: be able to set a cleanup function on a wait constraint

    let wait.On accept a func() that gets executed on failure

    e.g.

    s := TestServer 
    // ... 
    must.Wait(s.t, wait.On(
      wait.ErrorFunc(f),
      wait.Timeout(10*time.Second),
      wait.Gap(1*time.Second),
      wait.CleanupFailure(s.Stop), // new
    ), must.Sprint("failed to wait for leader"))
    
  • idea: helper for auto cleanup of tempfile

    idea: helper for auto cleanup of tempfile

    There is t.TempDir which creates a tmp directory and cleans itself up at the end of the test. It would be nice if there was something similar for a single file.

    var filename string
    must.TmpFile(t, "content", &filename) 
    
  • idea: built-in interpolations in string / error comparisons

    idea: built-in interpolations in string / error comparisons

    Kind of a general form of https://github.com/shoenig/test/issues/19 - what if we had builtin interpolations available, e.g. ${test.hostname}, so you could write test cases like

    must.EqError(t, "failed to connect to ${test.localhost}") 
    

    which would automagically work across IPv4/IPv6 systems

  • idea: test helper for localhost

    idea: test helper for localhost

    Sometimes we write code that encapsulates resolving localhost and the output of that code contains the resolved localhost address - which tends to vary depending on if/how IPv6 is configured. One environment may produce 127.0.0.1 while another [::1].

    A helper like Localhost(t *testing.T, s string) would be the most basic form, but there also tends to be cases where we want to include a port, and some cases with a protocol.

  • Recommend swapping the naming convention for Eq and EqOp

    Recommend swapping the naming convention for Eq and EqOp

    Eq is nice and short... why not reserve that for comparable values, and make the reflect-based version the longer of the two? I think you'll end up with people using .Eq for everything, and lose the benefit of using generics entirely.

Full-featured test framework for Go! Assertions, mocking, input testing, output capturing, and much more! 🍕
Full-featured test framework for Go! Assertions, mocking, input testing, output capturing, and much more! 🍕

testza ?? Testza is like pizza for Go - you could life without it, but why should you? Get The Module | Documentation | Contributing | Code of Conduct

Dec 10, 2022
A toolkit with common assertions and mocks that plays nicely with the standard library

Testify - Thou Shalt Write Tests ℹ️ We are working on testify v2 and would love to hear what you'd like to see in it, have your say here: https://cutt

Dec 30, 2022
Go module to validate Apple app attestations and assertions.

AppAttest Since iOS 14, Apple offers a new way to attest the integrity of a device. This is based on the WebAuthn specification. This go module implem

Nov 22, 2022
Assert to perform programming assertions

Cli EN README Assert para realizar las aserciones de programación. GoDoc godoc for github Menú Principal Se configura un menú con ese ejemplo como dis

Jan 13, 2022
A yaml data-driven testing format together with golang testing library

Specimen Yaml-based data-driven testing Specimen is a yaml data format for data-driven testing. This enforces separation between feature being tested

Nov 24, 2022
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

Dec 12, 2022
A generic fuzzing and delta-debugging framework
A generic fuzzing and delta-debugging framework

Tavor Tavor (Sindarin for woodpecker) is a framework for easily implementing and using fuzzing and delta-debugging. Its EBNF-like notation allows you

Dec 18, 2022
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.

Fortio Fortio (Φορτίο) started as, and is, Istio's load testing tool and now graduated to be its own project. Fortio is also used by, among others, Me

Jan 2, 2023
Library created for testing JSON against patterns.

Gomatch Library created for testing JSON against patterns. The goal was to be able to validate JSON focusing only on parts essential in given test cas

Oct 28, 2022
A Go library help testing your RESTful API application

RESTit A Go micro-framework to help writing RESTful API integration test Package RESTit provides helps to those who want to write an integration test

Oct 28, 2022
testcase is an opinionated behavior-driven-testing library

Table of Contents testcase Guide Official API Documentation Getting Started / Example Modules Summary DRY Modularization Stability Case Study About te

Nov 10, 2022
HTTP load testing tool and library. It's over 9000!
HTTP load testing tool and library. It's over 9000!

Vegeta Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate. It can be used both as a

Jan 7, 2023
A WebDriver client and acceptance testing library for Go
A WebDriver client and acceptance testing library for Go

Agouti Agouti is a library for writing browser-based acceptance tests in Google Go. It provides Gomega matchers and plays nicely with Ginkgo or Spec.

Dec 26, 2022
Go library for testing async behavior

Helpers for testing async behavior.

Jun 30, 2022
Gostresslib - A golang library for stress testing.

GoStressLib A golang library for stress testing. Install go get github.com/tenhan/gostresslib Usage package main import ( "github.com/tenhan/gostres

Nov 9, 2022
Tesuto - a little library for testing against HTTP services

tesuto import "github.com/guregu/tesuto" tesuto is a little library for testing

Jan 18, 2022
Expressive end-to-end HTTP API testing made easy in Go

baloo Expressive and versatile end-to-end HTTP API testing made easy in Go (golang), built on top of gentleman HTTP client toolkit. Take a look to the

Dec 13, 2022
Simple Go snapshot testing
Simple Go snapshot testing

Incredibly simple Go snapshot testing: cupaloy takes a snapshot of your test output and compares it to a snapshot committed alongside your tests. If t

Jan 5, 2023
Clean database for testing, inspired by database_cleaner for Ruby

DbCleaner Clean database for testing, inspired by database_cleaner for Ruby. It uses flock syscall under the hood to make sure the test can runs in pa

Nov 17, 2022