mirror of https://github.com/stretchr/testify.git
Merge branch 'master' into mockery_docs
commit
4f6e609334
|
@ -6,7 +6,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_version: ["1.18.1", "1.17.6", "1.16.5"]
|
||||
go_version: ["1.20", "1.19"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
name: Create release from new tag
|
||||
|
||||
# this flow will be run only when new tags are pushed that match our pattern
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create GitHub release from tag
|
||||
uses: softprops/action-gh-release@v1
|
|
@ -5,5 +5,4 @@ pull requests.
|
|||
|
||||
* @glesica
|
||||
* @boyan-soubachov
|
||||
* @mvdkleijn
|
||||
|
||||
|
|
30
README.md
30
README.md
|
@ -16,14 +16,14 @@ Features include:
|
|||
Get started:
|
||||
|
||||
* Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date)
|
||||
* For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing
|
||||
* Check out the API Documentation http://godoc.org/github.com/stretchr/testify
|
||||
* To make your testing life easier, check out our other project, [gorc](http://github.com/stretchr/gorc)
|
||||
* A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development)
|
||||
* For an introduction to writing test code in Go, see https://go.dev/doc/code#Testing
|
||||
* Check out the API Documentation https://pkg.go.dev/github.com/stretchr/testify
|
||||
* To make your testing life easier, check out our other project, [gorc](https://github.com/stretchr/gorc)
|
||||
* A little about [Test-Driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development)
|
||||
|
||||
|
||||
|
||||
[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package
|
||||
[`assert`](https://pkg.go.dev/github.com/stretchr/testify/assert "API documentation") package
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
The `assert` package provides some helpful methods that allow you to write better test code in Go.
|
||||
|
@ -100,19 +100,21 @@ func TestSomething(t *testing.T) {
|
|||
}
|
||||
```
|
||||
|
||||
[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package
|
||||
[`require`](https://pkg.go.dev/github.com/stretchr/testify/require "API documentation") package
|
||||
---------------------------------------------------------------------------------------------
|
||||
|
||||
The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test.
|
||||
These functions must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test.
|
||||
Otherwise race conditions may occur.
|
||||
|
||||
See [t.FailNow](http://golang.org/pkg/testing/#T.FailNow) for details.
|
||||
See [t.FailNow](https://pkg.go.dev/testing#T.FailNow) for details.
|
||||
|
||||
[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package
|
||||
[`mock`](https://pkg.go.dev/github.com/stretchr/testify/mock "API documentation") package
|
||||
----------------------------------------------------------------------------------------
|
||||
|
||||
The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.
|
||||
|
||||
An example test function that tests a piece of code that relies on an external object `testObj`, can setup expectations (testify) and assert that they indeed happened:
|
||||
An example test function that tests a piece of code that relies on an external object `testObj`, can set up expectations (testify) and assert that they indeed happened:
|
||||
|
||||
```go
|
||||
package yours
|
||||
|
@ -217,14 +219,14 @@ func TestSomethingElse2(t *testing.T) {
|
|||
}
|
||||
```
|
||||
|
||||
For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock).
|
||||
For more information on how to write mock code, check out the [API documentation for the `mock` package](https://pkg.go.dev/github.com/stretchr/testify/mock).
|
||||
|
||||
You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to autogenerate the mock code against an interface as well, making using mocks much quicker.
|
||||
|
||||
[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package
|
||||
[`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
The `suite` package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.
|
||||
The `suite` package provides functionality that you might be used to from more common object-oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.
|
||||
|
||||
An example suite is shown below:
|
||||
|
||||
|
@ -265,7 +267,7 @@ func TestExampleTestSuite(t *testing.T) {
|
|||
|
||||
For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go)
|
||||
|
||||
For more information on writing suites, check out the [API documentation for the `suite` package](http://godoc.org/github.com/stretchr/testify/suite).
|
||||
For more information on writing suites, check out the [API documentation for the `suite` package](https://pkg.go.dev/github.com/stretchr/testify/suite).
|
||||
|
||||
`Suite` object has assertion methods:
|
||||
|
||||
|
@ -348,7 +350,7 @@ To update Testify to the latest version, use `go get -u github.com/stretchr/test
|
|||
Supported go versions
|
||||
==================
|
||||
|
||||
We currently support the most recent major Go versions from 1.13 onward.
|
||||
We currently support the most recent major Go versions from 1.19 onward.
|
||||
|
||||
------
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
|||
break
|
||||
}
|
||||
|
||||
// time.Time can compared!
|
||||
// time.Time can be compared!
|
||||
timeObj1, ok := obj1.(time.Time)
|
||||
if !ok {
|
||||
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
|
||||
|
|
|
@ -90,6 +90,23 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
|||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
|
@ -155,6 +172,31 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
|
|||
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
|
|
|
@ -155,6 +155,40 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
|
|||
return EqualErrorf(a.t, theError, errString, msg, args...)
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true
|
||||
// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false
|
||||
func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
|
@ -288,6 +322,56 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
|
|||
return Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// a.EventuallyWithT(func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...)
|
||||
}
|
||||
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
|
||||
|
@ -75,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
|
|||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// copyExportedFields iterates downward through nested data structures and creates a copy
|
||||
// that only contains the exported struct fields.
|
||||
func copyExportedFields(expected interface{}) interface{} {
|
||||
if isNil(expected) {
|
||||
return expected
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(expected)
|
||||
expectedKind := expectedType.Kind()
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
|
||||
switch expectedKind {
|
||||
case reflect.Struct:
|
||||
result := reflect.New(expectedType).Elem()
|
||||
for i := 0; i < expectedType.NumField(); i++ {
|
||||
field := expectedType.Field(i)
|
||||
isExported := field.IsExported()
|
||||
if isExported {
|
||||
fieldValue := expectedValue.Field(i)
|
||||
if isNil(fieldValue) || isNil(fieldValue.Interface()) {
|
||||
continue
|
||||
}
|
||||
newValue := copyExportedFields(fieldValue.Interface())
|
||||
result.Field(i).Set(reflect.ValueOf(newValue))
|
||||
}
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
result := reflect.New(expectedType.Elem())
|
||||
unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface())
|
||||
result.Elem().Set(reflect.ValueOf(unexportedRemoved))
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
|
||||
for i := 0; i < expectedValue.Len(); i++ {
|
||||
index := expectedValue.Index(i)
|
||||
if isNil(index) {
|
||||
continue
|
||||
}
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.Index(i).Set(reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Map:
|
||||
result := reflect.MakeMap(expectedType)
|
||||
for _, k := range expectedValue.MapKeys() {
|
||||
index := expectedValue.MapIndex(k)
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
default:
|
||||
return expected
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are
|
||||
// considered equal. This comparison of only exported fields is applied recursively to nested data
|
||||
// structures.
|
||||
//
|
||||
// This function does no assertion of any kind.
|
||||
func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
|
||||
expectedCleaned := copyExportedFields(expected)
|
||||
actualCleaned := copyExportedFields(actual)
|
||||
return ObjectsAreEqualValues(expectedCleaned, actualCleaned)
|
||||
}
|
||||
|
||||
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||
// values are equal.
|
||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
||||
|
@ -195,7 +266,7 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
|||
|
||||
// Aligns the provided message so that all lines after the first line start at the same location as the first line.
|
||||
// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
|
||||
// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
|
||||
// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the
|
||||
// basis on which the alignment occurs).
|
||||
func indentMessageLines(message string, longestLabelLen int) string {
|
||||
outBuf := new(bytes.Buffer)
|
||||
|
@ -425,7 +496,7 @@ func samePointers(first, second interface{}) bool {
|
|||
// representations appropriate to be presented to the user.
|
||||
//
|
||||
// If the values are not of like type, the returned strings will be prefixed
|
||||
// with the type name, and the value will be enclosed in parenthesis similar
|
||||
// with the type name, and the value will be enclosed in parentheses similar
|
||||
// to a type conversion in the Go grammar.
|
||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
||||
|
@ -473,6 +544,50 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
|
|||
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
|
||||
func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
bType := reflect.TypeOf(actual)
|
||||
|
||||
if aType != bType {
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
|
||||
}
|
||||
|
||||
if aType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
expected = copyExportedFields(expected)
|
||||
actual = copyExportedFields(actual)
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
|
@ -794,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
|||
|
||||
ok, found := containsElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
if found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -816,49 +931,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
|||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
|
||||
if !av.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -877,34 +987,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
|||
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
if !av.IsValid() {
|
||||
return true
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -912,8 +1016,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
|||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
|
@ -1754,6 +1859,89 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
|||
}
|
||||
}
|
||||
|
||||
// CollectT implements the TestingT interface and collects all errors.
|
||||
type CollectT struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Errorf collects the error.
|
||||
func (c *CollectT) Errorf(format string, args ...interface{}) {
|
||||
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// FailNow panics.
|
||||
func (c *CollectT) FailNow() {
|
||||
panic("Assertion failed")
|
||||
}
|
||||
|
||||
// Reset clears the collected errors.
|
||||
func (c *CollectT) Reset() {
|
||||
c.errors = nil
|
||||
}
|
||||
|
||||
// Copy copies the collected errors to the supplied t.
|
||||
func (c *CollectT) Copy(t TestingT) {
|
||||
if tt, ok := t.(tHelper); ok {
|
||||
tt.Helper()
|
||||
}
|
||||
for _, err := range c.errors {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
collect := new(CollectT)
|
||||
ch := make(chan bool, 1)
|
||||
|
||||
timer := time.NewTimer(waitFor)
|
||||
defer timer.Stop()
|
||||
|
||||
ticker := time.NewTicker(tick)
|
||||
defer ticker.Stop()
|
||||
|
||||
for tick := ticker.C; ; {
|
||||
select {
|
||||
case <-timer.C:
|
||||
collect.Copy(t)
|
||||
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||
case <-tick:
|
||||
tick = nil
|
||||
collect.Reset()
|
||||
go func() {
|
||||
condition(collect)
|
||||
ch <- len(collect.errors) == 0
|
||||
}()
|
||||
case v := <-ch:
|
||||
if v {
|
||||
return true
|
||||
}
|
||||
tick = ticker.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
@ -149,6 +150,278 @@ func TestObjectsAreEqual(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
type Nested struct {
|
||||
Exported interface{}
|
||||
notExported interface{}
|
||||
}
|
||||
|
||||
type S struct {
|
||||
Exported1 interface{}
|
||||
Exported2 Nested
|
||||
notExported1 interface{}
|
||||
notExported2 Nested
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
foo interface{}
|
||||
}
|
||||
|
||||
type S3 struct {
|
||||
Exported1 *Nested
|
||||
Exported2 *Nested
|
||||
}
|
||||
|
||||
type S4 struct {
|
||||
Exported1 []*Nested
|
||||
}
|
||||
|
||||
type S5 struct {
|
||||
Exported Nested
|
||||
}
|
||||
|
||||
type S6 struct {
|
||||
Exported string
|
||||
unexported string
|
||||
}
|
||||
|
||||
func TestObjectsExportedFieldsAreEqual(t *testing.T) {
|
||||
|
||||
intValue := 1
|
||||
|
||||
cases := []struct {
|
||||
expected interface{}
|
||||
actual interface{}
|
||||
result bool
|
||||
}{
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{5, 6}}, true},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, "a", Nested{5, 6}}, true},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{5, "a"}}, true},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{"a", "a"}}, true},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, "a"}, 4, Nested{5, 6}}, true},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{"a", Nested{2, 3}, 4, Nested{5, 6}}, false},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{"a", 3}, 4, Nested{5, 6}}, false},
|
||||
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S2{1}, false},
|
||||
{1, S{1, Nested{2, 3}, 4, Nested{5, 6}}, false},
|
||||
|
||||
{S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, 2}, &Nested{3, 4}}, true},
|
||||
{S3{nil, &Nested{3, 4}}, S3{nil, &Nested{3, 4}}, true},
|
||||
{S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, 2}, &Nested{3, "b"}}, true},
|
||||
{S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, "a"}, &Nested{3, "b"}}, true},
|
||||
{S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{"a", 2}, &Nested{3, 4}}, false},
|
||||
{S3{&Nested{1, 2}, &Nested{3, 4}}, S3{}, false},
|
||||
{S3{}, S3{}, true},
|
||||
|
||||
{S4{[]*Nested{{1, 2}}}, S4{[]*Nested{{1, 2}}}, true},
|
||||
{S4{[]*Nested{{1, 2}}}, S4{[]*Nested{{1, 3}}}, true},
|
||||
{S4{[]*Nested{{1, 2}, {3, 4}}}, S4{[]*Nested{{1, "a"}, {3, "b"}}}, true},
|
||||
{S4{[]*Nested{{1, 2}, {3, 4}}}, S4{[]*Nested{{1, "a"}, {2, "b"}}}, false},
|
||||
|
||||
{Nested{&intValue, 2}, Nested{&intValue, 2}, true},
|
||||
{Nested{&Nested{1, 2}, 3}, Nested{&Nested{1, "b"}, 3}, true},
|
||||
{Nested{&Nested{1, 2}, 3}, Nested{nil, 3}, false},
|
||||
|
||||
{
|
||||
Nested{map[interface{}]*Nested{nil: nil}, 2},
|
||||
Nested{map[interface{}]*Nested{nil: nil}, 2},
|
||||
true,
|
||||
},
|
||||
{
|
||||
Nested{map[interface{}]*Nested{"a": nil}, 2},
|
||||
Nested{map[interface{}]*Nested{"a": nil}, 2},
|
||||
true,
|
||||
},
|
||||
{
|
||||
Nested{map[interface{}]*Nested{"a": nil}, 2},
|
||||
Nested{map[interface{}]*Nested{"a": {1, 2}}, 2},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Nested{map[interface{}]Nested{"a": {1, 2}, "b": {3, 4}}, 2},
|
||||
Nested{map[interface{}]Nested{"a": {1, 5}, "b": {3, 7}}, 2},
|
||||
true,
|
||||
},
|
||||
{
|
||||
Nested{map[interface{}]Nested{"a": {1, 2}, "b": {3, 4}}, 2},
|
||||
Nested{map[interface{}]Nested{"a": {2, 2}, "b": {3, 4}}, 2},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(fmt.Sprintf("ObjectsExportedFieldsAreEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
|
||||
res := ObjectsExportedFieldsAreEqual(c.expected, c.actual)
|
||||
|
||||
if res != c.result {
|
||||
t.Errorf("ObjectsExportedFieldsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyExportedFields(t *testing.T) {
|
||||
intValue := 1
|
||||
|
||||
cases := []struct {
|
||||
input interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
input: Nested{"a", "b"},
|
||||
expected: Nested{"a", nil},
|
||||
},
|
||||
{
|
||||
input: Nested{&intValue, 2},
|
||||
expected: Nested{&intValue, nil},
|
||||
},
|
||||
{
|
||||
input: Nested{nil, 3},
|
||||
expected: Nested{nil, nil},
|
||||
},
|
||||
{
|
||||
input: S{1, Nested{2, 3}, 4, Nested{5, 6}},
|
||||
expected: S{1, Nested{2, nil}, nil, Nested{}},
|
||||
},
|
||||
{
|
||||
input: S3{},
|
||||
expected: S3{},
|
||||
},
|
||||
{
|
||||
input: S3{&Nested{1, 2}, &Nested{3, 4}},
|
||||
expected: S3{&Nested{1, nil}, &Nested{3, nil}},
|
||||
},
|
||||
{
|
||||
input: S3{Exported1: &Nested{"a", "b"}},
|
||||
expected: S3{Exported1: &Nested{"a", nil}},
|
||||
},
|
||||
{
|
||||
input: S4{[]*Nested{
|
||||
nil,
|
||||
{1, 2},
|
||||
}},
|
||||
expected: S4{[]*Nested{
|
||||
nil,
|
||||
{1, nil},
|
||||
}},
|
||||
},
|
||||
{
|
||||
input: S4{[]*Nested{
|
||||
{1, 2}},
|
||||
},
|
||||
expected: S4{[]*Nested{
|
||||
{1, nil}},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: S4{[]*Nested{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
}},
|
||||
expected: S4{[]*Nested{
|
||||
{1, nil},
|
||||
{3, nil},
|
||||
}},
|
||||
},
|
||||
{
|
||||
input: S5{Exported: Nested{"a", "b"}},
|
||||
expected: S5{Exported: Nested{"a", nil}},
|
||||
},
|
||||
{
|
||||
input: S6{"a", "b"},
|
||||
expected: S6{"a", ""},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
output := copyExportedFields(c.input)
|
||||
if !ObjectsAreEqualValues(c.expected, output) {
|
||||
t.Errorf("%#v, %#v should be equal", c.expected, output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqualExportedValues(t *testing.T) {
|
||||
cases := []struct {
|
||||
value1 interface{}
|
||||
value2 interface{}
|
||||
expectedEqual bool
|
||||
expectedFail string
|
||||
}{
|
||||
{
|
||||
value1: S{1, Nested{2, 3}, 4, Nested{5, 6}},
|
||||
value2: S{1, Nested{2, nil}, nil, Nested{}},
|
||||
expectedEqual: true,
|
||||
},
|
||||
{
|
||||
value1: S{1, Nested{2, 3}, 4, Nested{5, 6}},
|
||||
value2: S{1, Nested{1, nil}, nil, Nested{}},
|
||||
expectedEqual: false,
|
||||
expectedFail: `
|
||||
Diff:
|
||||
--- Expected
|
||||
+++ Actual
|
||||
@@ -3,3 +3,3 @@
|
||||
Exported2: (assert.Nested) {
|
||||
- Exported: (int) 2,
|
||||
+ Exported: (int) 1,
|
||||
notExported: (interface {}) <nil>`,
|
||||
},
|
||||
{
|
||||
value1: S3{&Nested{1, 2}, &Nested{3, 4}},
|
||||
value2: S3{&Nested{"a", 2}, &Nested{3, 4}},
|
||||
expectedEqual: false,
|
||||
expectedFail: `
|
||||
Diff:
|
||||
--- Expected
|
||||
+++ Actual
|
||||
@@ -2,3 +2,3 @@
|
||||
Exported1: (*assert.Nested)({
|
||||
- Exported: (int) 1,
|
||||
+ Exported: (string) (len=1) "a",
|
||||
notExported: (interface {}) <nil>`,
|
||||
},
|
||||
{
|
||||
value1: S4{[]*Nested{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
}},
|
||||
value2: S4{[]*Nested{
|
||||
{1, "a"},
|
||||
{2, "b"},
|
||||
}},
|
||||
expectedEqual: false,
|
||||
expectedFail: `
|
||||
Diff:
|
||||
--- Expected
|
||||
+++ Actual
|
||||
@@ -7,3 +7,3 @@
|
||||
(*assert.Nested)({
|
||||
- Exported: (int) 3,
|
||||
+ Exported: (int) 2,
|
||||
notExported: (interface {}) <nil>`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
mockT := new(mockTestingT)
|
||||
|
||||
actual := EqualExportedValues(mockT, c.value1, c.value2)
|
||||
if actual != c.expectedEqual {
|
||||
t.Errorf("Expected EqualExportedValues to be %t, but was %t", c.expectedEqual, actual)
|
||||
}
|
||||
|
||||
actualFail := mockT.errorString()
|
||||
if !strings.Contains(actualFail, c.expectedFail) {
|
||||
t.Errorf("Contains failure should include %q but was %q", c.expectedFail, actualFail)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestImplements(t *testing.T) {
|
||||
|
||||
mockT := new(testing.T)
|
||||
|
@ -643,15 +916,59 @@ func TestContainsNotContains(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContainsFailMessage(t *testing.T) {
|
||||
|
||||
func TestContainsNotContainsFailMessage(t *testing.T) {
|
||||
mockT := new(mockTestingT)
|
||||
|
||||
Contains(mockT, "Hello World", errors.New("Hello"))
|
||||
expectedFail := "\"Hello World\" does not contain &errors.errorString{s:\"Hello\"}"
|
||||
type nonContainer struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
assertion func(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool
|
||||
container interface{}
|
||||
instance interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
assertion: Contains,
|
||||
container: "Hello World",
|
||||
instance: errors.New("Hello"),
|
||||
expected: "\"Hello World\" does not contain &errors.errorString{s:\"Hello\"}",
|
||||
},
|
||||
{
|
||||
assertion: Contains,
|
||||
container: map[string]int{"one": 1},
|
||||
instance: "two",
|
||||
expected: "map[string]int{\"one\":1} does not contain \"two\"\n",
|
||||
},
|
||||
{
|
||||
assertion: NotContains,
|
||||
container: map[string]int{"one": 1},
|
||||
instance: "one",
|
||||
expected: "map[string]int{\"one\":1} should not contain \"one\"",
|
||||
},
|
||||
{
|
||||
assertion: Contains,
|
||||
container: nonContainer{Value: "Hello"},
|
||||
instance: "Hello",
|
||||
expected: "assert.nonContainer{Value:\"Hello\"} could not be applied builtin len()\n",
|
||||
},
|
||||
{
|
||||
assertion: NotContains,
|
||||
container: nonContainer{Value: "Hello"},
|
||||
instance: "Hello",
|
||||
expected: "assert.nonContainer{Value:\"Hello\"} could not be applied builtin len()\n",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
name := filepath.Base(runtime.FuncForPC(reflect.ValueOf(c.assertion).Pointer()).Name())
|
||||
t.Run(fmt.Sprintf("%v(%T, %T)", name, c.container, c.instance), func(t *testing.T) {
|
||||
c.assertion(mockT, c.container, c.instance)
|
||||
actualFail := mockT.errorString()
|
||||
if !strings.Contains(actualFail, expectedFail) {
|
||||
t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
|
||||
if !strings.Contains(actualFail, c.expected) {
|
||||
t.Errorf("Contains failure should include %q but was %q", c.expected, actualFail)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,20 +989,18 @@ func TestContainsNotContainsOnNilValue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSubsetNotSubset(t *testing.T) {
|
||||
|
||||
// MTestCase adds a custom message to the case
|
||||
cases := []struct {
|
||||
expected interface{}
|
||||
actual interface{}
|
||||
list interface{}
|
||||
subset interface{}
|
||||
result bool
|
||||
message string
|
||||
}{
|
||||
// cases that are expected to contain
|
||||
{[]int{1, 2, 3}, nil, true, "given subset is nil"},
|
||||
{[]int{1, 2, 3}, []int{}, true, "any set contains the nil set"},
|
||||
{[]int{1, 2, 3}, []int{1, 2}, true, "[1, 2, 3] contains [1, 2]"},
|
||||
{[]int{1, 2, 3}, []int{1, 2, 3}, true, "[1, 2, 3] contains [1, 2, 3"},
|
||||
{[]string{"hello", "world"}, []string{"hello"}, true, "[\"hello\", \"world\"] contains [\"hello\"]"},
|
||||
{[]int{1, 2, 3}, nil, true, `nil is the empty set which is a subset of every set`},
|
||||
{[]int{1, 2, 3}, []int{}, true, `[] is a subset of ['\x01' '\x02' '\x03']`},
|
||||
{[]int{1, 2, 3}, []int{1, 2}, true, `['\x01' '\x02'] is a subset of ['\x01' '\x02' '\x03']`},
|
||||
{[]int{1, 2, 3}, []int{1, 2, 3}, true, `['\x01' '\x02' '\x03'] is a subset of ['\x01' '\x02' '\x03']`},
|
||||
{[]string{"hello", "world"}, []string{"hello"}, true, `["hello"] is a subset of ["hello" "world"]`},
|
||||
{map[string]string{
|
||||
"a": "x",
|
||||
"c": "z",
|
||||
|
@ -693,12 +1008,12 @@ func TestSubsetNotSubset(t *testing.T) {
|
|||
}, map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
}, true, `{ "a": "x", "b": "y", "c": "z"} contains { "a": "x", "b": "y"}`},
|
||||
}, true, `map["a":"x" "b":"y"] is a subset of map["a":"x" "b":"y" "c":"z"]`},
|
||||
|
||||
// cases that are expected not to contain
|
||||
{[]string{"hello", "world"}, []string{"hello", "testify"}, false, "[\"hello\", \"world\"] does not contain [\"hello\", \"testify\"]"},
|
||||
{[]int{1, 2, 3}, []int{4, 5}, false, "[1, 2, 3] does not contain [4, 5"},
|
||||
{[]int{1, 2, 3}, []int{1, 5}, false, "[1, 2, 3] does not contain [1, 5]"},
|
||||
{[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`},
|
||||
{[]int{1, 2, 3}, []int{4, 5}, false, `[]int{1, 2, 3} does not contain 4`},
|
||||
{[]int{1, 2, 3}, []int{1, 5}, false, `[]int{1, 2, 3} does not contain 5`},
|
||||
{map[string]string{
|
||||
"a": "x",
|
||||
"c": "z",
|
||||
|
@ -706,35 +1021,51 @@ func TestSubsetNotSubset(t *testing.T) {
|
|||
}, map[string]string{
|
||||
"a": "x",
|
||||
"b": "z",
|
||||
}, false, `{ "a": "x", "b": "y", "c": "z"} does not contain { "a": "x", "b": "z"}`},
|
||||
}, false, `map[string]string{"a":"x", "b":"y", "c":"z"} does not contain map[string]string{"a":"x", "b":"z"}`},
|
||||
{map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
}, map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
"c": "z",
|
||||
}, false, `map[string]string{"a":"x", "b":"y"} does not contain map[string]string{"a":"x", "b":"y", "c":"z"}`},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run("SubSet: "+c.message, func(t *testing.T) {
|
||||
|
||||
mockT := new(testing.T)
|
||||
res := Subset(mockT, c.expected, c.actual)
|
||||
mockT := new(mockTestingT)
|
||||
res := Subset(mockT, c.list, c.subset)
|
||||
|
||||
if res != c.result {
|
||||
if res {
|
||||
t.Errorf("Subset should return true: %s", c.message)
|
||||
} else {
|
||||
t.Errorf("Subset should return false: %s", c.message)
|
||||
t.Errorf("Subset should return %t: %s", c.result, c.message)
|
||||
}
|
||||
if !c.result {
|
||||
expectedFail := c.message
|
||||
actualFail := mockT.errorString()
|
||||
if !strings.Contains(actualFail, expectedFail) {
|
||||
t.Log(actualFail)
|
||||
t.Errorf("Subset failure should contain %q but was %q", expectedFail, actualFail)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run("NotSubSet: "+c.message, func(t *testing.T) {
|
||||
mockT := new(testing.T)
|
||||
res := NotSubset(mockT, c.expected, c.actual)
|
||||
mockT := new(mockTestingT)
|
||||
res := NotSubset(mockT, c.list, c.subset)
|
||||
|
||||
// NotSubset should match the inverse of Subset. If it doesn't, something is wrong
|
||||
if res == Subset(mockT, c.expected, c.actual) {
|
||||
if res {
|
||||
t.Errorf("NotSubset should return true: %s", c.message)
|
||||
} else {
|
||||
t.Errorf("NotSubset should return false: %s", c.message)
|
||||
if res == Subset(mockT, c.list, c.subset) {
|
||||
t.Errorf("NotSubset should return %t: %s", !c.result, c.message)
|
||||
}
|
||||
if c.result {
|
||||
expectedFail := c.message
|
||||
actualFail := mockT.errorString()
|
||||
if !strings.Contains(actualFail, expectedFail) {
|
||||
t.Log(actualFail)
|
||||
t.Errorf("NotSubset failure should contain %q but was %q", expectedFail, actualFail)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2429,6 +2760,32 @@ func TestEventuallyTrue(t *testing.T) {
|
|||
True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||
}
|
||||
|
||||
func TestEventuallyWithTFalse(t *testing.T) {
|
||||
mockT := new(CollectT)
|
||||
|
||||
condition := func(collect *CollectT) {
|
||||
True(collect, false)
|
||||
}
|
||||
|
||||
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||
Len(t, mockT.errors, 2)
|
||||
}
|
||||
|
||||
func TestEventuallyWithTTrue(t *testing.T) {
|
||||
mockT := new(CollectT)
|
||||
|
||||
state := 0
|
||||
condition := func(collect *CollectT) {
|
||||
defer func() {
|
||||
state += 1
|
||||
}()
|
||||
True(collect, state == 2)
|
||||
}
|
||||
|
||||
True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||
Len(t, mockT.errors, 0)
|
||||
}
|
||||
|
||||
func TestNeverFalse(t *testing.T) {
|
||||
condition := func() bool {
|
||||
return false
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using assert in a standard test function:
|
||||
//
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
|
@ -33,7 +34,7 @@
|
|||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
// # Assertions
|
||||
//
|
||||
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
|
||||
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
|
||||
|
|
4
go.mod
4
go.mod
|
@ -1,6 +1,8 @@
|
|||
module github.com/stretchr/testify
|
||||
|
||||
go 1.13
|
||||
// This should match the minimum supported version that is tested in
|
||||
// .github/workflows/main.yml
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Package mock provides a system by which it is possible to mock your objects
|
||||
// and verify calls are happening as expected.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
|
||||
// embedded into a test object as shown below:
|
||||
|
|
136
mock/mock.go
136
mock/mock.go
|
@ -3,6 +3,7 @@ package mock
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/stretchr/objx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -109,7 +111,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call {
|
|||
return c
|
||||
}
|
||||
|
||||
// Panic specifies if the functon call should fail and the panic message
|
||||
// Panic specifies if the function call should fail and the panic message
|
||||
//
|
||||
// Mock.On("DoSomething").Panic("test panic")
|
||||
func (c *Call) Panic(msg string) *Call {
|
||||
|
@ -121,21 +123,21 @@ func (c *Call) Panic(msg string) *Call {
|
|||
return c
|
||||
}
|
||||
|
||||
// Once indicates that that the mock should only return the value once.
|
||||
// Once indicates that the mock should only return the value once.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||
func (c *Call) Once() *Call {
|
||||
return c.Times(1)
|
||||
}
|
||||
|
||||
// Twice indicates that that the mock should only return the value twice.
|
||||
// Twice indicates that the mock should only return the value twice.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||
func (c *Call) Twice() *Call {
|
||||
return c.Times(2)
|
||||
}
|
||||
|
||||
// Times indicates that that the mock should only return the indicated number
|
||||
// Times indicates that the mock should only return the indicated number
|
||||
// of times.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||
|
@ -197,12 +199,14 @@ func (c *Call) Maybe() *Call {
|
|||
// Mock.
|
||||
// On("MyMethod", 1).Return(nil).
|
||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||
//
|
||||
//go:noinline
|
||||
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
||||
return c.Parent.On(methodName, arguments...)
|
||||
}
|
||||
|
||||
// Unset removes a mock handler from being called.
|
||||
//
|
||||
// test.On("func", mock.Anything).Unset()
|
||||
func (c *Call) Unset() *Call {
|
||||
var unlockOnce sync.Once
|
||||
|
@ -424,6 +428,10 @@ func callString(method string, arguments Arguments, includeArgumentValues bool)
|
|||
if includeArgumentValues {
|
||||
var argVals []string
|
||||
for argIndex, arg := range arguments {
|
||||
if _, ok := arg.(*FunctionalOptionsArgument); ok {
|
||||
argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg))
|
||||
continue
|
||||
}
|
||||
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
|
||||
}
|
||||
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
|
||||
|
@ -466,7 +474,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
|||
found, call := m.findExpectedCall(methodName, arguments...)
|
||||
|
||||
if found < 0 {
|
||||
// expected call found but it has already been called with repeatable times
|
||||
// expected call found, but it has already been called with repeatable times
|
||||
if call != nil {
|
||||
m.mutex.Unlock()
|
||||
m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo())
|
||||
|
@ -555,7 +563,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
|||
Assertions
|
||||
*/
|
||||
|
||||
type assertExpectationser interface {
|
||||
type assertExpectationiser interface {
|
||||
AssertExpectations(TestingT) bool
|
||||
}
|
||||
|
||||
|
@ -572,7 +580,7 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
|||
t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
|
||||
obj = m
|
||||
}
|
||||
m := obj.(assertExpectationser)
|
||||
m := obj.(assertExpectationiser)
|
||||
if !m.AssertExpectations(t) {
|
||||
t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
|
||||
return false
|
||||
|
@ -758,6 +766,7 @@ type AnythingOfTypeArgument string
|
|||
// name of the type to check for. Used in Diff and Assert.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
||||
func AnythingOfType(t string) AnythingOfTypeArgument {
|
||||
return AnythingOfTypeArgument(t)
|
||||
|
@ -780,6 +789,34 @@ func IsType(t interface{}) *IsTypeArgument {
|
|||
return &IsTypeArgument{t: t}
|
||||
}
|
||||
|
||||
// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument
|
||||
// for use when type checking.
|
||||
type FunctionalOptionsArgument struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// String returns the string representation of FunctionalOptionsArgument
|
||||
func (f *FunctionalOptionsArgument) String() string {
|
||||
var name string
|
||||
tValue := reflect.ValueOf(f.value)
|
||||
if tValue.Len() > 0 {
|
||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
||||
}
|
||||
|
||||
return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1)
|
||||
}
|
||||
|
||||
// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type
|
||||
// and the values to check of
|
||||
//
|
||||
// For example:
|
||||
// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2()))
|
||||
func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument {
|
||||
return &FunctionalOptionsArgument{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// argumentMatcher performs custom argument matching, returning whether or
|
||||
// not the argument is matched by the expectation fixture function.
|
||||
type argumentMatcher struct {
|
||||
|
@ -926,6 +963,29 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
|
|||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt)
|
||||
}
|
||||
} else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) {
|
||||
t := expected.(*FunctionalOptionsArgument).value
|
||||
|
||||
var name string
|
||||
tValue := reflect.ValueOf(t)
|
||||
if tValue.Len() > 0 {
|
||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
||||
}
|
||||
|
||||
tName := reflect.TypeOf(t).Name()
|
||||
if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
|
||||
} else {
|
||||
if ef, af := assertOpts(t, actual); ef == "" && af == "" {
|
||||
// match
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
|
||||
} else {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal checking
|
||||
|
||||
|
@ -1102,3 +1162,65 @@ var spewConfig = spew.ConfigState{
|
|||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) {
|
||||
expectedOpts := reflect.ValueOf(expected)
|
||||
actualOpts := reflect.ValueOf(actual)
|
||||
var expectedNames []string
|
||||
for i := 0; i < expectedOpts.Len(); i++ {
|
||||
expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface()))
|
||||
}
|
||||
var actualNames []string
|
||||
for i := 0; i < actualOpts.Len(); i++ {
|
||||
actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface()))
|
||||
}
|
||||
if !assert.ObjectsAreEqual(expectedNames, actualNames) {
|
||||
expectedFmt = fmt.Sprintf("%v", expectedNames)
|
||||
actualFmt = fmt.Sprintf("%v", actualNames)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < expectedOpts.Len(); i++ {
|
||||
expectedOpt := expectedOpts.Index(i).Interface()
|
||||
actualOpt := actualOpts.Index(i).Interface()
|
||||
|
||||
expectedFunc := expectedNames[i]
|
||||
actualFunc := actualNames[i]
|
||||
if expectedFunc != actualFunc {
|
||||
expectedFmt = expectedFunc
|
||||
actualFmt = actualFunc
|
||||
return
|
||||
}
|
||||
|
||||
ot := reflect.TypeOf(expectedOpt)
|
||||
var expectedValues []reflect.Value
|
||||
var actualValues []reflect.Value
|
||||
if ot.NumIn() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < ot.NumIn(); i++ {
|
||||
vt := ot.In(i).Elem()
|
||||
expectedValues = append(expectedValues, reflect.New(vt))
|
||||
actualValues = append(actualValues, reflect.New(vt))
|
||||
}
|
||||
|
||||
reflect.ValueOf(expectedOpt).Call(expectedValues)
|
||||
reflect.ValueOf(actualOpt).Call(actualValues)
|
||||
|
||||
for i := 0; i < ot.NumIn(); i++ {
|
||||
if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) {
|
||||
expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface())
|
||||
actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func funcName(opt interface{}) string {
|
||||
n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name()
|
||||
return strings.TrimSuffix(path.Base(n), path.Ext(n))
|
||||
}
|
||||
|
|
|
@ -32,6 +32,29 @@ func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) {
|
|||
return args.Int(0), errors.New("Whoops")
|
||||
}
|
||||
|
||||
type options struct {
|
||||
num int
|
||||
str string
|
||||
}
|
||||
|
||||
type OptionFn func(*options)
|
||||
|
||||
func OpNum(n int) OptionFn {
|
||||
return func(o *options) {
|
||||
o.num = n
|
||||
}
|
||||
}
|
||||
|
||||
func OpStr(s string) OptionFn {
|
||||
return func(o *options) {
|
||||
o.str = s
|
||||
}
|
||||
}
|
||||
func (i *TestExampleImplementation) TheExampleMethodFunctionalOptions(x string, opts ...OptionFn) error {
|
||||
args := i.Called(x, opts)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) {
|
||||
i.Called(yesorno)
|
||||
|
@ -1415,6 +1438,40 @@ func Test_Mock_AssertExpectationsCustomType(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func Test_Mock_AssertExpectationsFunctionalOptionsType(t *testing.T) {
|
||||
|
||||
var mockedService = new(TestExampleImplementation)
|
||||
|
||||
mockedService.On("TheExampleMethodFunctionalOptions", "test", FunctionalOptions(OpNum(1), OpStr("foo"))).Return(nil).Once()
|
||||
|
||||
tt := new(testing.T)
|
||||
assert.False(t, mockedService.AssertExpectations(tt))
|
||||
|
||||
// make the call now
|
||||
mockedService.TheExampleMethodFunctionalOptions("test", OpNum(1), OpStr("foo"))
|
||||
|
||||
// now assert expectations
|
||||
assert.True(t, mockedService.AssertExpectations(tt))
|
||||
|
||||
}
|
||||
|
||||
func Test_Mock_AssertExpectationsFunctionalOptionsType_Empty(t *testing.T) {
|
||||
|
||||
var mockedService = new(TestExampleImplementation)
|
||||
|
||||
mockedService.On("TheExampleMethodFunctionalOptions", "test", FunctionalOptions()).Return(nil).Once()
|
||||
|
||||
tt := new(testing.T)
|
||||
assert.False(t, mockedService.AssertExpectations(tt))
|
||||
|
||||
// make the call now
|
||||
mockedService.TheExampleMethodFunctionalOptions("test")
|
||||
|
||||
// now assert expectations
|
||||
assert.True(t, mockedService.AssertExpectations(tt))
|
||||
|
||||
}
|
||||
|
||||
func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) {
|
||||
|
||||
var mockedService = new(TestExampleImplementation)
|
||||
|
@ -1599,7 +1656,7 @@ func Test_Mock_AssertOptional(t *testing.T) {
|
|||
}
|
||||
|
||||
/*
|
||||
Arguments helper methods
|
||||
Arguments helper methods
|
||||
*/
|
||||
func Test_Arguments_Get(t *testing.T) {
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Package require implements the same assertions as the `assert` package but
|
||||
// stops test execution when a test fails.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using require in a standard test function:
|
||||
//
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
|
@ -18,7 +19,7 @@
|
|||
//
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
// # Assertions
|
||||
//
|
||||
// The `require` package have same global functions as in the `assert` package,
|
||||
// but instead of returning a boolean result they call `t.FailNow()`.
|
||||
|
|
|
@ -195,6 +195,46 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
|
||||
func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.EqualExportedValuesf(t, expected, actual, msg, args...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
|
@ -364,6 +404,62 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
|
|
|
@ -156,6 +156,40 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
|
|||
EqualErrorf(a.t, theError, errString, msg, args...)
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true
|
||||
// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false
|
||||
func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
EqualExportedValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
EqualExportedValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
|
@ -289,6 +323,56 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
|
|||
Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// a.EventuallyWithT(func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...)
|
||||
}
|
||||
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
// Suite object has assertion methods.
|
||||
//
|
||||
// A crude example:
|
||||
//
|
||||
// // Basic imports
|
||||
// import (
|
||||
// "testing"
|
||||
|
|
Loading…
Reference in New Issue