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
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_version: ["1.18.1", "1.17.6", "1.16.5"]
|
go_version: ["1.20", "1.19"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Go
|
- 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
|
* @glesica
|
||||||
* @boyan-soubachov
|
* @boyan-soubachov
|
||||||
* @mvdkleijn
|
|
||||||
|
|
||||||
|
|
28
README.md
28
README.md
|
@ -16,14 +16,14 @@ Features include:
|
||||||
Get started:
|
Get started:
|
||||||
|
|
||||||
* Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date)
|
* 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
|
* For an introduction to writing test code in Go, see https://go.dev/doc/code#Testing
|
||||||
* Check out the API Documentation http://godoc.org/github.com/stretchr/testify
|
* 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](http://github.com/stretchr/gorc)
|
* To make your testing life easier, check out our other project, [gorc](https://github.com/stretchr/gorc)
|
||||||
* A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development)
|
* 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.
|
The `assert` package provides some helpful methods that allow you to write better test code in Go.
|
||||||
|
@ -100,14 +100,16 @@ 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.
|
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.
|
The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.
|
||||||
|
@ -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.
|
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:
|
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 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:
|
`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
|
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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// time.Time can compared!
|
// time.Time can be compared!
|
||||||
timeObj1, ok := obj1.(time.Time)
|
timeObj1, ok := obj1.(time.Time)
|
||||||
if !ok {
|
if !ok {
|
||||||
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
|
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...)...)
|
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
|
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||||
// and equal.
|
// 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...)...)
|
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.
|
// Exactlyf asserts that two objects are equal in value and type.
|
||||||
//
|
//
|
||||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
// 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...)
|
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
|
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||||
// and equal.
|
// 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...)
|
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,
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
// periodically checking target function each tick.
|
// periodically checking target function each tick.
|
||||||
//
|
//
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"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"
|
//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)
|
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
|
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||||
// values are equal.
|
// values are equal.
|
||||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
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.
|
// 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).
|
// 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).
|
// basis on which the alignment occurs).
|
||||||
func indentMessageLines(message string, longestLabelLen int) string {
|
func indentMessageLines(message string, longestLabelLen int) string {
|
||||||
outBuf := new(bytes.Buffer)
|
outBuf := new(bytes.Buffer)
|
||||||
|
@ -425,7 +496,7 @@ func samePointers(first, second interface{}) bool {
|
||||||
// representations appropriate to be presented to the user.
|
// representations appropriate to be presented to the user.
|
||||||
//
|
//
|
||||||
// If the values are not of like type, the returned strings will be prefixed
|
// 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.
|
// to a type conversion in the Go grammar.
|
||||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
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.
|
// Exactly asserts that two objects are equal in value and type.
|
||||||
//
|
//
|
||||||
// assert.Exactly(t, int32(123), int64(123))
|
// 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)
|
ok, found := containsElement(s, contains)
|
||||||
if !ok {
|
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 {
|
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
|
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
|
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()
|
listKind := reflect.TypeOf(list).Kind()
|
||||||
subsetKind := reflect.TypeOf(subset).Kind()
|
|
||||||
|
|
||||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
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 {
|
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
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 {
|
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||||
listValue := reflect.ValueOf(list)
|
subsetMap := reflect.ValueOf(subset)
|
||||||
subsetKeys := subsetValue.MapKeys()
|
actualMap := reflect.ValueOf(list)
|
||||||
|
|
||||||
for i := 0; i < len(subsetKeys); i++ {
|
for _, k := range subsetMap.MapKeys() {
|
||||||
subsetKey := subsetKeys[i]
|
ev := subsetMap.MapIndex(k)
|
||||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
av := actualMap.MapIndex(k)
|
||||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
|
||||||
|
|
||||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
if !av.IsValid() {
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < subsetValue.Len(); i++ {
|
subsetList := reflect.ValueOf(subset)
|
||||||
element := subsetValue.Index(i).Interface()
|
for i := 0; i < subsetList.Len(); i++ {
|
||||||
|
element := subsetList.Index(i).Interface()
|
||||||
ok, found := containsElement(list, element)
|
ok, found := containsElement(list, element)
|
||||||
if !ok {
|
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 {
|
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...)
|
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()
|
listKind := reflect.TypeOf(list).Kind()
|
||||||
subsetKind := reflect.TypeOf(subset).Kind()
|
|
||||||
|
|
||||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
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 {
|
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
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 {
|
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||||
listValue := reflect.ValueOf(list)
|
subsetMap := reflect.ValueOf(subset)
|
||||||
subsetKeys := subsetValue.MapKeys()
|
actualMap := reflect.ValueOf(list)
|
||||||
|
|
||||||
for i := 0; i < len(subsetKeys); i++ {
|
for _, k := range subsetMap.MapKeys() {
|
||||||
subsetKey := subsetKeys[i]
|
ev := subsetMap.MapIndex(k)
|
||||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
av := actualMap.MapIndex(k)
|
||||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
|
||||||
|
|
||||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
if !av.IsValid() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||||
return true
|
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...)
|
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < subsetValue.Len(); i++ {
|
subsetList := reflect.ValueOf(subset)
|
||||||
element := subsetValue.Index(i).Interface()
|
for i := 0; i < subsetList.Len(); i++ {
|
||||||
|
element := subsetList.Index(i).Interface()
|
||||||
ok, found := containsElement(list, element)
|
ok, found := containsElement(list, element)
|
||||||
if !ok {
|
if !ok {
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
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,
|
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||||
// periodically checking the target function each tick.
|
// periodically checking the target function each tick.
|
||||||
//
|
//
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"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) {
|
func TestImplements(t *testing.T) {
|
||||||
|
|
||||||
mockT := new(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)
|
mockT := new(mockTestingT)
|
||||||
|
|
||||||
Contains(mockT, "Hello World", errors.New("Hello"))
|
type nonContainer struct {
|
||||||
expectedFail := "\"Hello World\" does not contain &errors.errorString{s:\"Hello\"}"
|
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()
|
actualFail := mockT.errorString()
|
||||||
if !strings.Contains(actualFail, expectedFail) {
|
if !strings.Contains(actualFail, c.expected) {
|
||||||
t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
|
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) {
|
func TestSubsetNotSubset(t *testing.T) {
|
||||||
|
|
||||||
// MTestCase adds a custom message to the case
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
expected interface{}
|
list interface{}
|
||||||
actual interface{}
|
subset interface{}
|
||||||
result bool
|
result bool
|
||||||
message string
|
message string
|
||||||
}{
|
}{
|
||||||
// cases that are expected to contain
|
// cases that are expected to contain
|
||||||
{[]int{1, 2, 3}, nil, true, "given subset is nil"},
|
{[]int{1, 2, 3}, nil, true, `nil is the empty set which is a subset of every set`},
|
||||||
{[]int{1, 2, 3}, []int{}, true, "any set contains the nil set"},
|
{[]int{1, 2, 3}, []int{}, true, `[] is a subset of ['\x01' '\x02' '\x03']`},
|
||||||
{[]int{1, 2, 3}, []int{1, 2}, true, "[1, 2, 3] contains [1, 2]"},
|
{[]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, "[1, 2, 3] contains [1, 2, 3"},
|
{[]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\", \"world\"] contains [\"hello\"]"},
|
{[]string{"hello", "world"}, []string{"hello"}, true, `["hello"] is a subset of ["hello" "world"]`},
|
||||||
{map[string]string{
|
{map[string]string{
|
||||||
"a": "x",
|
"a": "x",
|
||||||
"c": "z",
|
"c": "z",
|
||||||
|
@ -693,12 +1008,12 @@ func TestSubsetNotSubset(t *testing.T) {
|
||||||
}, map[string]string{
|
}, map[string]string{
|
||||||
"a": "x",
|
"a": "x",
|
||||||
"b": "y",
|
"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
|
// cases that are expected not to contain
|
||||||
{[]string{"hello", "world"}, []string{"hello", "testify"}, false, "[\"hello\", \"world\"] does not contain [\"hello\", \"testify\"]"},
|
{[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`},
|
||||||
{[]int{1, 2, 3}, []int{4, 5}, false, "[1, 2, 3] does not contain [4, 5"},
|
{[]int{1, 2, 3}, []int{4, 5}, false, `[]int{1, 2, 3} does not contain 4`},
|
||||||
{[]int{1, 2, 3}, []int{1, 5}, false, "[1, 2, 3] does not contain [1, 5]"},
|
{[]int{1, 2, 3}, []int{1, 5}, false, `[]int{1, 2, 3} does not contain 5`},
|
||||||
{map[string]string{
|
{map[string]string{
|
||||||
"a": "x",
|
"a": "x",
|
||||||
"c": "z",
|
"c": "z",
|
||||||
|
@ -706,35 +1021,51 @@ func TestSubsetNotSubset(t *testing.T) {
|
||||||
}, map[string]string{
|
}, map[string]string{
|
||||||
"a": "x",
|
"a": "x",
|
||||||
"b": "z",
|
"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 {
|
for _, c := range cases {
|
||||||
t.Run("SubSet: "+c.message, func(t *testing.T) {
|
t.Run("SubSet: "+c.message, func(t *testing.T) {
|
||||||
|
|
||||||
mockT := new(testing.T)
|
mockT := new(mockTestingT)
|
||||||
res := Subset(mockT, c.expected, c.actual)
|
res := Subset(mockT, c.list, c.subset)
|
||||||
|
|
||||||
if res != c.result {
|
if res != c.result {
|
||||||
if res {
|
t.Errorf("Subset should return %t: %s", c.result, c.message)
|
||||||
t.Errorf("Subset should return true: %s", c.message)
|
}
|
||||||
} else {
|
if !c.result {
|
||||||
t.Errorf("Subset should return false: %s", c.message)
|
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 {
|
for _, c := range cases {
|
||||||
t.Run("NotSubSet: "+c.message, func(t *testing.T) {
|
t.Run("NotSubSet: "+c.message, func(t *testing.T) {
|
||||||
mockT := new(testing.T)
|
mockT := new(mockTestingT)
|
||||||
res := NotSubset(mockT, c.expected, c.actual)
|
res := NotSubset(mockT, c.list, c.subset)
|
||||||
|
|
||||||
// NotSubset should match the inverse of Subset. If it doesn't, something is wrong
|
// NotSubset should match the inverse of Subset. If it doesn't, something is wrong
|
||||||
if res == Subset(mockT, c.expected, c.actual) {
|
if res == Subset(mockT, c.list, c.subset) {
|
||||||
if res {
|
t.Errorf("NotSubset should return %t: %s", !c.result, c.message)
|
||||||
t.Errorf("NotSubset should return true: %s", c.message)
|
}
|
||||||
} else {
|
if c.result {
|
||||||
t.Errorf("NotSubset should return false: %s", c.message)
|
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))
|
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) {
|
func TestNeverFalse(t *testing.T) {
|
||||||
condition := func() bool {
|
condition := func() bool {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
|
// 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:
|
// The following is a complete example using assert in a standard test function:
|
||||||
|
//
|
||||||
// import (
|
// import (
|
||||||
// "testing"
|
// "testing"
|
||||||
// "github.com/stretchr/testify/assert"
|
// "github.com/stretchr/testify/assert"
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
// assert.Equal(a, b, "The two words should be the same.")
|
// 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.
|
// 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
|
// 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
|
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 (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
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
|
// Package mock provides a system by which it is possible to mock your objects
|
||||||
// and verify calls are happening as expected.
|
// 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
|
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
|
||||||
// embedded into a test object as shown below:
|
// embedded into a test object as shown below:
|
||||||
|
|
136
mock/mock.go
136
mock/mock.go
|
@ -3,6 +3,7 @@ package mock
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
"github.com/stretchr/objx"
|
"github.com/stretchr/objx"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -109,7 +111,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call {
|
||||||
return c
|
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")
|
// Mock.On("DoSomething").Panic("test panic")
|
||||||
func (c *Call) Panic(msg string) *Call {
|
func (c *Call) Panic(msg string) *Call {
|
||||||
|
@ -121,21 +123,21 @@ func (c *Call) Panic(msg string) *Call {
|
||||||
return c
|
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()
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||||
func (c *Call) Once() *Call {
|
func (c *Call) Once() *Call {
|
||||||
return c.Times(1)
|
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()
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||||
func (c *Call) Twice() *Call {
|
func (c *Call) Twice() *Call {
|
||||||
return c.Times(2)
|
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.
|
// of times.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||||
|
@ -197,12 +199,14 @@ func (c *Call) Maybe() *Call {
|
||||||
// Mock.
|
// Mock.
|
||||||
// On("MyMethod", 1).Return(nil).
|
// On("MyMethod", 1).Return(nil).
|
||||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||||
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
||||||
return c.Parent.On(methodName, arguments...)
|
return c.Parent.On(methodName, arguments...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unset removes a mock handler from being called.
|
// Unset removes a mock handler from being called.
|
||||||
|
//
|
||||||
// test.On("func", mock.Anything).Unset()
|
// test.On("func", mock.Anything).Unset()
|
||||||
func (c *Call) Unset() *Call {
|
func (c *Call) Unset() *Call {
|
||||||
var unlockOnce sync.Once
|
var unlockOnce sync.Once
|
||||||
|
@ -424,6 +428,10 @@ func callString(method string, arguments Arguments, includeArgumentValues bool)
|
||||||
if includeArgumentValues {
|
if includeArgumentValues {
|
||||||
var argVals []string
|
var argVals []string
|
||||||
for argIndex, arg := range arguments {
|
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))
|
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
|
||||||
}
|
}
|
||||||
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
|
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...)
|
found, call := m.findExpectedCall(methodName, arguments...)
|
||||||
|
|
||||||
if found < 0 {
|
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 {
|
if call != nil {
|
||||||
m.mutex.Unlock()
|
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())
|
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
|
Assertions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type assertExpectationser interface {
|
type assertExpectationiser interface {
|
||||||
AssertExpectations(TestingT) bool
|
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)")
|
t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
|
||||||
obj = m
|
obj = m
|
||||||
}
|
}
|
||||||
m := obj.(assertExpectationser)
|
m := obj.(assertExpectationiser)
|
||||||
if !m.AssertExpectations(t) {
|
if !m.AssertExpectations(t) {
|
||||||
t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
|
t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
|
||||||
return false
|
return false
|
||||||
|
@ -758,6 +766,7 @@ type AnythingOfTypeArgument string
|
||||||
// name of the type to check for. Used in Diff and Assert.
|
// name of the type to check for. Used in Diff and Assert.
|
||||||
//
|
//
|
||||||
// For example:
|
// For example:
|
||||||
|
//
|
||||||
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
||||||
func AnythingOfType(t string) AnythingOfTypeArgument {
|
func AnythingOfType(t string) AnythingOfTypeArgument {
|
||||||
return AnythingOfTypeArgument(t)
|
return AnythingOfTypeArgument(t)
|
||||||
|
@ -780,6 +789,34 @@ func IsType(t interface{}) *IsTypeArgument {
|
||||||
return &IsTypeArgument{t: t}
|
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
|
// argumentMatcher performs custom argument matching, returning whether or
|
||||||
// not the argument is matched by the expectation fixture function.
|
// not the argument is matched by the expectation fixture function.
|
||||||
type argumentMatcher struct {
|
type argumentMatcher struct {
|
||||||
|
@ -926,6 +963,29 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
|
||||||
differences++
|
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)
|
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 {
|
} else {
|
||||||
// normal checking
|
// normal checking
|
||||||
|
|
||||||
|
@ -1102,3 +1162,65 @@ var spewConfig = spew.ConfigState{
|
||||||
type tHelper interface {
|
type tHelper interface {
|
||||||
Helper()
|
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")
|
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
|
//go:noinline
|
||||||
func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) {
|
func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) {
|
||||||
i.Called(yesorno)
|
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) {
|
func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) {
|
||||||
|
|
||||||
var mockedService = new(TestExampleImplementation)
|
var mockedService = new(TestExampleImplementation)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// Package require implements the same assertions as the `assert` package but
|
// Package require implements the same assertions as the `assert` package but
|
||||||
// stops test execution when a test fails.
|
// stops test execution when a test fails.
|
||||||
//
|
//
|
||||||
// Example Usage
|
// # Example Usage
|
||||||
//
|
//
|
||||||
// The following is a complete example using require in a standard test function:
|
// The following is a complete example using require in a standard test function:
|
||||||
|
//
|
||||||
// import (
|
// import (
|
||||||
// "testing"
|
// "testing"
|
||||||
// "github.com/stretchr/testify/require"
|
// "github.com/stretchr/testify/require"
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Assertions
|
// # Assertions
|
||||||
//
|
//
|
||||||
// The `require` package have same global functions as in the `assert` package,
|
// The `require` package have same global functions as in the `assert` package,
|
||||||
// but instead of returning a boolean result they call `t.FailNow()`.
|
// 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()
|
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
|
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||||
// and equal.
|
// and equal.
|
||||||
//
|
//
|
||||||
|
@ -364,6 +404,62 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
||||||
t.FailNow()
|
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,
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
// periodically checking target function each tick.
|
// 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...)
|
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
|
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||||
// and equal.
|
// and equal.
|
||||||
//
|
//
|
||||||
|
@ -289,6 +323,56 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
|
||||||
Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
|
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,
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
// periodically checking target function each tick.
|
// periodically checking target function each tick.
|
||||||
//
|
//
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
// Suite object has assertion methods.
|
// Suite object has assertion methods.
|
||||||
//
|
//
|
||||||
// A crude example:
|
// A crude example:
|
||||||
|
//
|
||||||
// // Basic imports
|
// // Basic imports
|
||||||
// import (
|
// import (
|
||||||
// "testing"
|
// "testing"
|
||||||
|
|
Loading…
Reference in New Issue