Documentation ¶
Overview ¶
Package matchers provides a flexible test assertion API similar to Java's Hamcrest. Matchers are constructed separately from the values being tested, and can then be applied to any value, or negated, or combined in various ways.
This implementation is for Go 1.17 so it does not yet have generics. Instead, all matchers take values of type interface{} and must explicitly cast the type if needed. The simplest way to provide type safety is to use Matcher.EnsureType().
Examples of syntax:
import m "github.com/launchdarkly/go-test-helpers/matchers" func TestSomething(t *T) { eventData := []string{ `{"kind": "feature", "value": true}`, `{"key": "x", "kind": "custom"}`, } m.For(t, "event data").Assert(eventData, m.ItemsInAnyOrder( m.JSONStrEqual(`{"kind": "custom", "key": "x"}`), m.JSONStrEqual(`{"kind": "feature", "value": true}`), )) m.For(t, "first event").Assert(eventData[0], m.JSONProperty("kind").Should(m.Not(m.Equal("summary")))) }
Index ¶
- func DescribeValue(value interface{}) string
- type AssertionScope
- type DescribeFailureFunc
- type DescribeTestFunc
- type KeyValueMatcher
- type Matcher
- func AllOf(matchers ...Matcher) Matcher
- func AnyOf(matchers ...Matcher) Matcher
- func BeNil() Matcher
- func Equal(expectedValue interface{}) Matcher
- func Items(matchers ...Matcher) Matcher
- func ItemsInAnyOrder(matchers ...Matcher) Matcher
- func JSONEqual(expectedValue interface{}) Matcher
- func JSONStrEqual(expectedValue string) Matcher
- func MapIncluding(keyValueMatchers ...KeyValueMatcher) Matcher
- func MapOf(keyValueMatchers ...KeyValueMatcher) Matcher
- func New(test TestFunc, describeTest DescribeTestFunc, ...) Matcher
- func Not(matcher Matcher) Matcher
- func StringContains(substring string) Matcher
- func StringHasPrefix(prefix string) Matcher
- func StringHasSuffix(suffix string) Matcher
- type MatcherTransform
- func JSONArray() MatcherTransform
- func JSONMap() MatcherTransform
- func JSONOptProperty(name string) MatcherTransform
- func JSONProperty(name string) MatcherTransform
- func Length() MatcherTransform
- func OptValueForKey(key interface{}) MatcherTransform
- func Transform(name string, getValue func(interface{}) (interface{}, error)) MatcherTransform
- func ValueForKey(key interface{}) MatcherTransform
- type TestFunc
- type TestingT
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DescribeValue ¶
func DescribeValue(value interface{}) string
DescribeValue tries to create attractive string representations of values for test failure messages. The logic is as follows (whichever comes first):
If the value is nil, it returns "nil".
If the type is a struct that has "json" field tags, it is converted to JSON.
If the type implements fmt.Stringer, its String method is called.
If the type is string, it is quoted, unless it already has bracket or brace delimiters.
If the type is []byte, it is converted to a string unchanged, unless it is valid JSON in which case it is passed to jsonhelpers.CanonicalizeJSON.
If the type is json.RawMessage, it is passed to jsonhelpers.CanonicalizeJSON.
If the type is a slice or array, it is formatted as [value1, value2, value3] (unlike Go's default formatting which has no commas) and each value is recursively formatted with DescribeValue.
At last resort, it is formatted with fmt.Sprintf("%+v").
Types ¶
type AssertionScope ¶
type AssertionScope struct {
// contains filtered or unexported fields
}
AssertionScope is a context for executing assertions.
func For ¶
func For(t TestingT, name string) AssertionScope
For is the same as In, but adds a descriptive name in front of whatever assertions are done. In this example, a failure would be logged as "score: does not equal 2" rather than only "does not equal 2".
func TestSomething(t *testing.T) { matchers.For(t, "score").Assert(x, matchers.Equal(2)) }
func In ¶
func In(t TestingT) AssertionScope
In is for use with any test framework that has a test scope type with the same basic methods as Go's testing.T (as defined by the TestingT interface). Any calls to Assert or Require on the returned AssertionScope will update the state of t.
func TestSomething(t *testing.T) { matchers.In(t).Assert(x, matchers.Equal(2)) }
See also For.
func (AssertionScope) Assert ¶
func (a AssertionScope) Assert(value interface{}, matcher Matcher) bool
Assert is for use with any test framework that has a test scope type with the same Errorf method as Go's testing.T. It tests a value against a matcher and, on failure, calls the test scope's Errorf method. This logs a failure but does not stop the test.
func (AssertionScope) For ¶
func (a AssertionScope) For(name string) AssertionScope
For returns a new AssertionScope that has an additional name prefix. In this example, a failure would be logged as "final: score: does not equal 2" rather than only "does not equal 2".
func TestSomething(t *testing.T) { matchers.In(t).For("final").For("score").Assert(x, matchers.Equal(2)) }
func (AssertionScope) Require ¶
func (a AssertionScope) Require(value interface{}, matcher Matcher) bool
Require is for use with any test framework that has a test scope type with the same Errorf and FailNow methods as Go's testing.T. It tests a value against a matcher and, on failure, calls the test scope's Errorf method and then FailNow. This logs a failure and immediately terminates the test.
type DescribeFailureFunc ¶
type DescribeFailureFunc func(value interface{}) string
DescribeFailureFunc is a function used in defining a new Matcher. Given the value that was tested, and assuming that the test failed, it returns a descriptive string.
For simple conditions, this function can be omitted or can return an empty string, in which case the failure description will be produced from only the DescribeTestFunc and a description of the test value
The second parameter is the function to use for making a string description of a value of the expected type.
type DescribeTestFunc ¶
type DescribeTestFunc func() string
DescribeTestFunc is a function used in defining a new Matcher. It returns a description of the test expectation.
type KeyValueMatcher ¶
type KeyValueMatcher struct { Key interface{} Value Matcher }
KeyValueMatcher is used with MapOf or MapIncluding to describe a matcher for a key-value pair in a map.
func KV ¶
func KV(key interface{}, valueMatcher Matcher) KeyValueMatcher
KV is a shortcut for constructing a KeyValueMatcher for use with MapOf or MapIncluding.
type Matcher ¶
type Matcher struct {
// contains filtered or unexported fields
}
Matcher is a general mechanism for declaring expectations about a value. Expectations can be combined, and they self-describe on failure.
func AllOf ¶
AllOf requires that the input value passes all of the specified Matchers. If it fails, the failure message describes all of the Matchers that failed.
func AnyOf ¶
AnyOf requires that the input value passes at least one of the specified Matchers. It will not execute any further matches after the first pass. If it fails all of them, the failure message describes all of the failure conditions.
func BeNil ¶
func BeNil() Matcher
BeNil is a matcher that passes if the value is a nil interface value, a nil pointer, a nil slice, or a nil map.
func Equal ¶
func Equal(expectedValue interface{}) Matcher
Equal is a matcher that tests whether the input value matches the expected value according to reflect.DeepEqual, except in the case of numbers where an exact type match is not needed.
func Items ¶
Items is a matcher for a slice or array value. It tests that the number of elements is equal to the number of matchers, and that each element matches the corresponding matcher in order.
s := []int{6,2} matchers.Items(matchers.Equal(6), matchers.Equal(2)).Test(s) // pass matchers.Items(matchers.Equal(2), matchers.Equal(6)).Test(s) // fail
func ItemsInAnyOrder ¶
ItemsInAnyOrder is a matcher for a slice or array value. It tests that the number of elements is equal to the number of matchers, and that each matcher matches an element.
s := []int{6,2} matchers.ItemsInAnyOrder(matchers.Equal(2), matchers.Equal(6)).Test(s) // pass
func JSONEqual ¶
func JSONEqual(expectedValue interface{}) Matcher
JSONEqual is similar to Equal but with richer behavior for JSON values.
Both the expected value and the actual value can be of any type. If the type is either []byte or json.RawMessage, it will be interpreted as JSON which will be parsed; for all other types, it will be first serialized to JSON with json.Marshal and then parsed. Then the parsed values or data structures are tested for deep equality. For instance, this test passes:
matchers.In(t).Assert([]byte(`{"a": true, "b": false`), matchers.JSONEqual(map[string]bool{b: false, a: true}))
The shortcut JSONEqualStr can be used to avoid writing []byte() if the expected value is already a serialized JSON string.
func JSONStrEqual ¶
JSONStrEqual is equivalent to JSONEqual except that it converts expectedValue from string to []byte first, and if the input value is a string it does the same. This is convenient if you are matching against already-serialized JSON, because otherwise passing a string value to JSONEqual would cause that value to be serialized in the way JSON represents strings, that is, with quoting and escaping.
matchers.In(t).Assert(`{"a": true, "b": false`, matchers.JSONStrEqual(`{"b": false, "a": true}`)
func MapIncluding ¶
func MapIncluding(keyValueMatchers ...KeyValueMatcher) Matcher
MapIncluding is a matcher for a map value. It tests that the map contains all of the keys in the specified list, and that the matcher for each key is satisfied by the corresponding value. The map may also contain additional keys.
m := map[string]int{"a": 6, "b": 2} matchers.MapOf( matchers.KV("a", matchers.Equal(2)), matchers.KV("b", matchers.Equal(6)), }).Test(s) // pass
func MapOf ¶
func MapOf(keyValueMatchers ...KeyValueMatcher) Matcher
MapOf is a matcher for a map value. It tests that the map has exactly the same keys as the specified list, and that the matcher for each key is satisfied by the corresponding value.
m := map[string]int{"a": 6, "b": 2} matchers.MapOf( matchers.KV("a", matchers.Equal(2)), matchers.KV("b", matchers.Equal(6)), }).Test(s) // pass
func New ¶
func New( test TestFunc, describeTest DescribeTestFunc, describeFailure DescribeFailureFunc, ) Matcher
New creates a Matcher.
func Not ¶
Not negates the result of another Matcher.
matchers.Not(Equal(3)).Assert(t, 4) // failure message will describe expectation as "not (equal to 3)"
func StringContains ¶
StringContains is a matcher for string values that tests for the presence of a substring, case-sensitively.
func StringHasPrefix ¶
StringHasPrefix is a matcher for string values that calls strings.HasPrefix.
func StringHasSuffix ¶
StringHasSuffix is a matcher for string values that calls strings.HasSuffix.
func (Matcher) EnsureType ¶
EnsureType adds type safety to a matcher. The valueOfType parameter should be any value of the expected type. The returned Matcher will guarantee that the value is of that type before calling the original test function, so it is safe for the test function to cast the value.
type MatcherTransform ¶
type MatcherTransform struct {
// contains filtered or unexported fields
}
MatcherTransform is a combinator that allows an input value to be transformed to some other value (possibly of a different type) before being tested by other Matchers.
For instance, this could be used to access a field inside a struct or some other nested data structure. Assuming there is a struct type S with a field F, you could do this:
SF := matchers.Transform("F", func(value interface{}) interface{} { return value.(S).F }) SF.Should(Equal(3)).Assert(t, someInstanceOfS)
The advantages of doing this, instead of simply getting the F field directly and testing it, are 1. you can use combinators such as AllOf to test multiple properties in a single assertion, and 2. failure messages will automatically include both a full description of someInstanceOfS and an explanation of what was wrong with it. For instance, in the example above, if someInstanceOfS.F was really 4, the failure message would show:
expected: F equal to 3 actual value was: {F: 4}
You can use MatcherTransform's other methods to add type safety.
func JSONArray ¶
func JSONArray() MatcherTransform
JSONArray is a MatcherTransform that takes any value serializable as a JSON array, and converts it to []interface{} slice; then you can apply a matcher to that slice. It fails if the value is not serializable as a JSON array.
myArray := []byte(`["a", "b", "c"]`) matchers.In(t).Assert(myArray, matchers.JSONArray().Should(matchers.Length().Should(matchers.Equal(3))))
func JSONMap ¶
func JSONMap() MatcherTransform
JSONMap is a MatcherTransform that takes any value serializable as a JSON object, and converts it to a map[interface{}]interface{}; then you can apply a matcher to that map. It fails if the value is not serializable as a JSON object.
myArray := []byte(`{"a": 1, "b": "xyz"}`) matchers.In(t).Assert(myJSON, matchers.JSONMap().Should( matchers.MapOf( matchers.KV("a", matchers.Equal(1)), matchers.KV("b", matchers.StringHasPrefix("x")), )))
func JSONOptProperty ¶
func JSONOptProperty(name string) MatcherTransform
JSONOptProperty is the same as JSONProperty, but if the property does not exist, it treats it as a nil value rather than error.
func JSONProperty ¶
func JSONProperty(name string) MatcherTransform
JSONProperty is a MatcherTransform that takes any value serializable as a JSON object and gets a named property from it; then you can apply a matcher to the value of that property. It fails if no such property exists (see OptJSONProperty).
myObject := []byte(`{"a": {"b": 2}}`) matchers.In(t).Assert(myObject, matchers.JSONProperty("a").Should( matchers.JSONProperty("b").Should(Equal(2))))
An alternative is to use JSONMap combined with MapOf or MapIncluding.
func Length ¶
func Length() MatcherTransform
Length is a MatcherTransform that takes any value len() can operate on, gets its length, and applies some matcher to the result.
func OptValueForKey ¶
func OptValueForKey(key interface{}) MatcherTransform
OptValueForKey is a MatcherTransform that takes a map, looks up a value in it by key, and applies a matcher to that value. If no such key exists, it returns the zero value for the type. If the map was nil, it returns nil.
myMap := map[string]map[string]int{"a": map[string]int{"b": 2}} matchers.In(t).Assert(myMap, matchers.OptValueForKey("a").Should( matchers.OptValueForKey("c").Should(Equal(0))))
func Transform ¶
func Transform( name string, getValue func(interface{}) (interface{}, error), ) MatcherTransform
Transform creates a MatcherTransform. The name parameter is a brief description of what the output value is in relation to the input value (for instance, if you are getting a field called F from a struct, it could simply be "F"); it will be prefixed to the description of any Matcher that you use with Should(). The getValue parameter is a function that transforms the original value into the value you will be testing.
func ValueForKey ¶
func ValueForKey(key interface{}) MatcherTransform
ValueForKey is a MatcherTransform that takes a map, looks up a value in it by key, and applies a matcher to that value. It fails if no such key exists (see OptValueForKey).
myMap := map[string]map[string]int{"a": map[string]int{"b": 2}} matchers.In(t).Assert(myObject, matchers.ValueForKey("a").Should( matchers.ValueForKey("b").Should(Equal(2))))
func (MatcherTransform) EnsureInputValueType ¶
func (mt MatcherTransform) EnsureInputValueType(valueOfType interface{}) MatcherTransform
EnsureInputValueType is the equivalent of Matcher.EnsureValueType. Given any value of the desired type, it returns a modified MatcherTransform that will safely fail if the wrong type is passed in.
stringLength := matchers.Transform("string length", func(value interface{}) interface{} { return len(value.(string)) }). EnsureInputValueType("")
func (MatcherTransform) Should ¶
func (mt MatcherTransform) Should(matcher Matcher) Matcher
Should applies a Matcher to the transformed value. That is, assuming that this MatcherTransform converts an A value into a B value, mt.Should(Equal(3)) returns a Matcher that takes A, converts it to B, and applies Equal(3) to B.
type TestFunc ¶
type TestFunc func(value interface{}) bool
TestFunc is a function used in defining a new Matcher. It returns true if the value passes the test or false for failure.
type TestingT ¶
type TestingT interface { Errorf(format string, args ...interface{}) FailNow() }
TestingT is an interface for any test scope type that has an Errorf method for reporting failures, and a FailNow method for stopping the test immediately. This is compatible with Go's testing.T, and with assert.TestingT and require.TestingT. See Test and For.