mirror of https://github.com/stretchr/testify.git
Merge branch 'master' into fix/equal-exported-values-accepts-everything
commit
dce9e58ee3
|
@ -29,6 +29,7 @@ jobs:
|
||||||
- "1.19"
|
- "1.19"
|
||||||
- "1.20"
|
- "1.20"
|
||||||
- "1.21"
|
- "1.21"
|
||||||
|
- "1.22"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
|
|
|
@ -223,6 +223,8 @@ You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to auto
|
||||||
|
|
||||||
[`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package
|
[`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package
|
||||||
-----------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------
|
||||||
|
> [!WARNING]
|
||||||
|
> The suite package does not support parallel tests. See [#934](https://github.com/stretchr/testify/issues/934).
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,9 @@ func parseTemplates() (*template.Template, *template.Template, error) {
|
||||||
}
|
}
|
||||||
funcTemplate = string(f)
|
funcTemplate = string(f)
|
||||||
}
|
}
|
||||||
tmpl, err := template.New("function").Parse(funcTemplate)
|
tmpl, err := template.New("function").Funcs(template.FuncMap{
|
||||||
|
"replace": strings.ReplaceAll,
|
||||||
|
}).Parse(funcTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,6 +568,23 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
|
||||||
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
|
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
|
||||||
|
func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||||
// a slice or a channel with len == 0.
|
// a slice or a channel with len == 0.
|
||||||
//
|
//
|
||||||
|
@ -604,7 +621,16 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
|
||||||
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
// NotErrorAsf asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorIsf asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
|
|
|
@ -1128,6 +1128,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
|
||||||
return NotContainsf(a.t, s, contains, msg, args...)
|
return NotContainsf(a.t, s, contains, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true
|
||||||
|
func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotElementsMatch(a.t, listA, listB, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
|
||||||
|
func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotElementsMatchf(a.t, listA, listB, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||||
// a slice or a channel with len == 0.
|
// a slice or a channel with len == 0.
|
||||||
//
|
//
|
||||||
|
@ -1200,7 +1234,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
|
||||||
return NotEqualf(a.t, expected, actual, msg, args...)
|
return NotEqualf(a.t, expected, actual, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
// NotErrorAs asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorAs(a.t, err, target, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorAsf asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return NotErrorAsf(a.t, err, target, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorIs asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
@ -1209,7 +1261,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface
|
||||||
return NotErrorIs(a.t, err, target, msgAndArgs...)
|
return NotErrorIs(a.t, err, target, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
// NotErrorIsf asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
|
|
@ -1161,6 +1161,39 @@ func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) stri
|
||||||
return msg.String()
|
return msg.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true
|
||||||
|
//
|
||||||
|
// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true
|
||||||
|
func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
if isEmpty(listA) && isEmpty(listB) {
|
||||||
|
return Fail(t, "listA and listB contain the same elements", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isList(t, listA, msgAndArgs...) {
|
||||||
|
return Fail(t, "listA is not a list type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
if !isList(t, listB, msgAndArgs...) {
|
||||||
|
return Fail(t, "listB is not a list type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
extraA, extraB := diffLists(listA, listB)
|
||||||
|
if len(extraA) == 0 && len(extraB) == 0 {
|
||||||
|
return Fail(t, "listA and listB contain the same elements", msgAndArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Condition uses a Comparison to assert a complex condition.
|
// Condition uses a Comparison to assert a complex condition.
|
||||||
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
|
@ -1908,6 +1941,9 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
||||||
|
|
||||||
// CollectT implements the TestingT interface and collects all errors.
|
// CollectT implements the TestingT interface and collects all errors.
|
||||||
type CollectT struct {
|
type CollectT struct {
|
||||||
|
// A slice of errors. Non-nil slice denotes a failure.
|
||||||
|
// If it's non-nil but len(c.errors) == 0, this is also a failure
|
||||||
|
// obtained by direct c.FailNow() call.
|
||||||
errors []error
|
errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1916,9 +1952,10 @@ func (c *CollectT) Errorf(format string, args ...interface{}) {
|
||||||
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailNow panics.
|
// FailNow stops execution by calling runtime.Goexit.
|
||||||
func (*CollectT) FailNow() {
|
func (c *CollectT) FailNow() {
|
||||||
panic("Assertion failed")
|
c.fail()
|
||||||
|
runtime.Goexit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
||||||
|
@ -1931,6 +1968,16 @@ func (*CollectT) Copy(TestingT) {
|
||||||
panic("Copy() is deprecated")
|
panic("Copy() is deprecated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CollectT) fail() {
|
||||||
|
if !c.failed() {
|
||||||
|
c.errors = []error{} // Make it non-nil to mark a failure.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CollectT) failed() bool {
|
||||||
|
return c.errors != nil
|
||||||
|
}
|
||||||
|
|
||||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||||
// periodically checking target function each tick. In contrast to Eventually,
|
// periodically checking target function each tick. In contrast to Eventually,
|
||||||
// it supplies a CollectT to the condition function, so that the condition
|
// it supplies a CollectT to the condition function, so that the condition
|
||||||
|
@ -1955,7 +2002,7 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastFinishedTickErrs []error
|
var lastFinishedTickErrs []error
|
||||||
ch := make(chan []error, 1)
|
ch := make(chan *CollectT, 1)
|
||||||
|
|
||||||
timer := time.NewTimer(waitFor)
|
timer := time.NewTimer(waitFor)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
|
@ -1975,16 +2022,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
|
||||||
go func() {
|
go func() {
|
||||||
collect := new(CollectT)
|
collect := new(CollectT)
|
||||||
defer func() {
|
defer func() {
|
||||||
ch <- collect.errors
|
ch <- collect
|
||||||
}()
|
}()
|
||||||
condition(collect)
|
condition(collect)
|
||||||
}()
|
}()
|
||||||
case errs := <-ch:
|
case collect := <-ch:
|
||||||
if len(errs) == 0 {
|
if !collect.failed() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
|
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
|
||||||
lastFinishedTickErrs = errs
|
lastFinishedTickErrs = collect.errors
|
||||||
tick = ticker.C
|
tick = ticker.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2046,7 +2093,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||||
), msgAndArgs...)
|
), msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
// NotErrorIs asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
|
||||||
if h, ok := t.(tHelper); ok {
|
if h, ok := t.(tHelper); ok {
|
||||||
|
@ -2087,6 +2134,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
|
||||||
), msgAndArgs...)
|
), msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotErrorAs asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
if !errors.As(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := buildErrorChainString(err)
|
||||||
|
|
||||||
|
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
|
||||||
|
"found: %q\n"+
|
||||||
|
"in chain: %s", target, chain,
|
||||||
|
), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
func buildErrorChainString(err error) string {
|
func buildErrorChainString(err error) string {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -1369,6 +1369,52 @@ func TestDiffLists(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotElementsMatch(t *testing.T) {
|
||||||
|
mockT := new(testing.T)
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
expected interface{}
|
||||||
|
actual interface{}
|
||||||
|
result bool
|
||||||
|
}{
|
||||||
|
// not matching
|
||||||
|
{[]int{1}, []int{}, true},
|
||||||
|
{[]int{}, []int{2}, true},
|
||||||
|
{[]int{1}, []int{2}, true},
|
||||||
|
{[]int{1}, []int{1, 1}, true},
|
||||||
|
{[]int{1, 2}, []int{3, 4}, true},
|
||||||
|
{[]int{3, 4}, []int{1, 2}, true},
|
||||||
|
{[]int{1, 1, 2, 3}, []int{1, 2, 3}, true},
|
||||||
|
{[]string{"hello"}, []string{"world"}, true},
|
||||||
|
{[]string{"hello", "hello"}, []string{"world", "world"}, true},
|
||||||
|
{[3]string{"hello", "hello", "hello"}, [3]string{"world", "world", "world"}, true},
|
||||||
|
|
||||||
|
// matching
|
||||||
|
{nil, nil, false},
|
||||||
|
{[]int{}, nil, false},
|
||||||
|
{[]int{}, []int{}, false},
|
||||||
|
{[]int{1}, []int{1}, false},
|
||||||
|
{[]int{1, 1}, []int{1, 1}, false},
|
||||||
|
{[]int{1, 2}, []int{2, 1}, false},
|
||||||
|
{[2]int{1, 2}, [2]int{2, 1}, false},
|
||||||
|
{[]int{1, 1, 2}, []int{1, 2, 1}, false},
|
||||||
|
{[]string{"hello", "world"}, []string{"world", "hello"}, false},
|
||||||
|
{[]string{"hello", "hello"}, []string{"hello", "hello"}, false},
|
||||||
|
{[]string{"hello", "hello", "world"}, []string{"hello", "world", "hello"}, false},
|
||||||
|
{[3]string{"hello", "hello", "world"}, [3]string{"hello", "world", "hello"}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("NotElementsMatch(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
|
||||||
|
res := NotElementsMatch(mockT, c.actual, c.expected)
|
||||||
|
|
||||||
|
if res != c.result {
|
||||||
|
t.Errorf("NotElementsMatch(%#v, %#v) should return %v", c.actual, c.expected, c.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCondition(t *testing.T) {
|
func TestCondition(t *testing.T) {
|
||||||
mockT := new(testing.T)
|
mockT := new(testing.T)
|
||||||
|
|
||||||
|
@ -2927,16 +2973,15 @@ func TestEventuallyWithTFalse(t *testing.T) {
|
||||||
func TestEventuallyWithTTrue(t *testing.T) {
|
func TestEventuallyWithTTrue(t *testing.T) {
|
||||||
mockT := new(errorsCapturingT)
|
mockT := new(errorsCapturingT)
|
||||||
|
|
||||||
state := 0
|
counter := 0
|
||||||
condition := func(collect *CollectT) {
|
condition := func(collect *CollectT) {
|
||||||
defer func() {
|
counter += 1
|
||||||
state += 1
|
True(collect, counter == 2)
|
||||||
}()
|
|
||||||
True(collect, state == 2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||||
Len(t, mockT.errors, 0)
|
Len(t, mockT.errors, 0)
|
||||||
|
Equal(t, 2, counter, "Condition is expected to be called 2 times")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEventuallyWithT_ConcurrencySafe(t *testing.T) {
|
func TestEventuallyWithT_ConcurrencySafe(t *testing.T) {
|
||||||
|
@ -2974,6 +3019,17 @@ func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) {
|
||||||
Len(t, mockT.errors, 2)
|
Len(t, mockT.errors, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEventuallyWithTFailNow(t *testing.T) {
|
||||||
|
mockT := new(CollectT)
|
||||||
|
|
||||||
|
condition := func(collect *CollectT) {
|
||||||
|
collect.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||||
|
Len(t, mockT.errors, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNeverFalse(t *testing.T) {
|
func TestNeverFalse(t *testing.T) {
|
||||||
condition := func() bool {
|
condition := func() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -3263,7 +3319,6 @@ func TestNotErrorIs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrorAs(t *testing.T) {
|
func TestErrorAs(t *testing.T) {
|
||||||
mockT := new(testing.T)
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
err error
|
err error
|
||||||
result bool
|
result bool
|
||||||
|
@ -3276,9 +3331,38 @@ func TestErrorAs(t *testing.T) {
|
||||||
tt := tt
|
tt := tt
|
||||||
var target *customError
|
var target *customError
|
||||||
t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
|
t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
|
||||||
|
mockT := new(testing.T)
|
||||||
res := ErrorAs(mockT, tt.err, &target)
|
res := ErrorAs(mockT, tt.err, &target)
|
||||||
if res != tt.result {
|
if res != tt.result {
|
||||||
t.Errorf("ErrorAs(%#v,%#v) should return %t)", tt.err, target, tt.result)
|
t.Errorf("ErrorAs(%#v,%#v) should return %t", tt.err, target, tt.result)
|
||||||
|
}
|
||||||
|
if res == mockT.Failed() {
|
||||||
|
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotErrorAs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
err error
|
||||||
|
result bool
|
||||||
|
}{
|
||||||
|
{fmt.Errorf("wrap: %w", &customError{}), false},
|
||||||
|
{io.EOF, true},
|
||||||
|
{nil, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
var target *customError
|
||||||
|
t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
|
||||||
|
mockT := new(testing.T)
|
||||||
|
res := NotErrorAs(mockT, tt.err, &target)
|
||||||
|
if res != tt.result {
|
||||||
|
t.Errorf("NotErrorAs(%#v,%#v) should not return %t", tt.err, target, tt.result)
|
||||||
|
}
|
||||||
|
if res == mockT.Failed() {
|
||||||
|
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// import assertYaml "github.com/stretchr/testify/assert/yaml"
|
// import assertYaml "github.com/stretchr/testify/assert/yaml"
|
||||||
//
|
//
|
||||||
// func init() {
|
// func init() {
|
||||||
// assertYaml.Unmarshall = func (in []byte, out interface{}) error {
|
// assertYaml.Unmarshal = func (in []byte, out interface{}) error {
|
||||||
// // ...
|
// // ...
|
||||||
// return nil
|
// return nil
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//
|
//
|
||||||
// This package is just an indirection that allows the builder to override the
|
// This package is just an indirection that allows the builder to override the
|
||||||
// indirection with an alternative implementation of this package that uses
|
// indirection with an alternative implementation of this package that uses
|
||||||
// another implemantation of YAML deserialization. This allows to not either not
|
// another implementation of YAML deserialization. This allows to not either not
|
||||||
// use YAML deserialization at all, or to use another implementation than
|
// use YAML deserialization at all, or to use another implementation than
|
||||||
// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]).
|
// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]).
|
||||||
//
|
//
|
||||||
|
|
60
mock/mock.go
60
mock/mock.go
|
@ -80,12 +80,12 @@ type Call struct {
|
||||||
requires []*Call
|
requires []*Call
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call {
|
func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments Arguments, returnArguments Arguments) *Call {
|
||||||
return &Call{
|
return &Call{
|
||||||
Parent: parent,
|
Parent: parent,
|
||||||
Method: methodName,
|
Method: methodName,
|
||||||
Arguments: methodArguments,
|
Arguments: methodArguments,
|
||||||
ReturnArguments: make([]interface{}, 0),
|
ReturnArguments: returnArguments,
|
||||||
callerInfo: callerInfo,
|
callerInfo: callerInfo,
|
||||||
Repeatability: 0,
|
Repeatability: 0,
|
||||||
WaitFor: nil,
|
WaitFor: nil,
|
||||||
|
@ -256,7 +256,7 @@ func (c *Call) Unset() *Call {
|
||||||
// calls have been called as expected. The referenced calls may be from the
|
// calls have been called as expected. The referenced calls may be from the
|
||||||
// same mock instance and/or other mock instances.
|
// same mock instance and/or other mock instances.
|
||||||
//
|
//
|
||||||
// Mock.On("Do").Return(nil).Notbefore(
|
// Mock.On("Do").Return(nil).NotBefore(
|
||||||
// Mock.On("Init").Return(nil)
|
// Mock.On("Init").Return(nil)
|
||||||
// )
|
// )
|
||||||
func (c *Call) NotBefore(calls ...*Call) *Call {
|
func (c *Call) NotBefore(calls ...*Call) *Call {
|
||||||
|
@ -273,6 +273,20 @@ func (c *Call) NotBefore(calls ...*Call) *Call {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InOrder defines the order in which the calls should be made
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// InOrder(
|
||||||
|
// Mock.On("init").Return(nil),
|
||||||
|
// Mock.On("Do").Return(nil),
|
||||||
|
// )
|
||||||
|
func InOrder(calls ...*Call) {
|
||||||
|
for i := 1; i < len(calls); i++ {
|
||||||
|
calls[i].NotBefore(calls[i-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mock is the workhorse used to track activity on another object.
|
// Mock is the workhorse used to track activity on another object.
|
||||||
// For an example of its usage, refer to the "Example Usage" section at the top
|
// For an example of its usage, refer to the "Example Usage" section at the top
|
||||||
// of this document.
|
// of this document.
|
||||||
|
@ -351,7 +365,8 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call {
|
||||||
|
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
c := newCall(m, methodName, assert.CallerInfo(), arguments...)
|
|
||||||
|
c := newCall(m, methodName, assert.CallerInfo(), arguments, make([]interface{}, 0))
|
||||||
m.ExpectedCalls = append(m.ExpectedCalls, c)
|
m.ExpectedCalls = append(m.ExpectedCalls, c)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -491,11 +506,12 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
|
||||||
if closestCall != nil {
|
if closestCall != nil {
|
||||||
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s",
|
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s\nat: %s\n",
|
||||||
callString(methodName, arguments, true),
|
callString(methodName, arguments, true),
|
||||||
callString(methodName, closestCall.Arguments, true),
|
callString(methodName, closestCall.Arguments, true),
|
||||||
diffArguments(closestCall.Arguments, arguments),
|
diffArguments(closestCall.Arguments, arguments),
|
||||||
strings.TrimSpace(mismatch),
|
strings.TrimSpace(mismatch),
|
||||||
|
assert.CallerInfo(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())
|
m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())
|
||||||
|
@ -529,7 +545,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
||||||
call.totalCalls++
|
call.totalCalls++
|
||||||
|
|
||||||
// add the call
|
// add the call
|
||||||
m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...))
|
m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments, call.ReturnArguments))
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
|
||||||
// block if specified
|
// block if specified
|
||||||
|
@ -811,21 +827,20 @@ func IsType(t interface{}) *IsTypeArgument {
|
||||||
return &IsTypeArgument{t: reflect.TypeOf(t)}
|
return &IsTypeArgument{t: reflect.TypeOf(t)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument
|
// FunctionalOptionsArgument contains a list of functional options arguments
|
||||||
// for use when type checking.
|
// expected for use when matching a list of arguments.
|
||||||
type FunctionalOptionsArgument struct {
|
type FunctionalOptionsArgument struct {
|
||||||
value interface{}
|
values []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the string representation of FunctionalOptionsArgument
|
// String returns the string representation of FunctionalOptionsArgument
|
||||||
func (f *FunctionalOptionsArgument) String() string {
|
func (f *FunctionalOptionsArgument) String() string {
|
||||||
var name string
|
var name string
|
||||||
tValue := reflect.ValueOf(f.value)
|
if len(f.values) > 0 {
|
||||||
if tValue.Len() > 0 {
|
name = "[]" + reflect.TypeOf(f.values[0]).String()
|
||||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1)
|
return strings.Replace(fmt.Sprintf("%#v", f.values), "[]interface {}", name, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FunctionalOptions returns an [FunctionalOptionsArgument] object containing
|
// FunctionalOptions returns an [FunctionalOptionsArgument] object containing
|
||||||
|
@ -833,10 +848,10 @@ func (f *FunctionalOptionsArgument) String() string {
|
||||||
//
|
//
|
||||||
// For example:
|
// For example:
|
||||||
//
|
//
|
||||||
// Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613)))
|
// args.Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613)))
|
||||||
func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument {
|
func FunctionalOptions(values ...interface{}) *FunctionalOptionsArgument {
|
||||||
return &FunctionalOptionsArgument{
|
return &FunctionalOptionsArgument{
|
||||||
value: value,
|
values: values,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,20 +1005,17 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
|
||||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
|
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
|
||||||
}
|
}
|
||||||
case *FunctionalOptionsArgument:
|
case *FunctionalOptionsArgument:
|
||||||
t := expected.value
|
|
||||||
|
|
||||||
var name string
|
var name string
|
||||||
tValue := reflect.ValueOf(t)
|
if len(expected.values) > 0 {
|
||||||
if tValue.Len() > 0 {
|
name = "[]" + reflect.TypeOf(expected.values[0]).String()
|
||||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tName := reflect.TypeOf(t).Name()
|
const tName = "[]interface{}"
|
||||||
if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
|
if name != reflect.TypeOf(actual).String() && len(expected.values) != 0 {
|
||||||
differences++
|
differences++
|
||||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
|
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
|
||||||
} else {
|
} else {
|
||||||
if ef, af := assertOpts(t, actual); ef == "" && af == "" {
|
if ef, af := assertOpts(expected.values, actual); ef == "" && af == "" {
|
||||||
// match
|
// match
|
||||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
|
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -937,6 +937,26 @@ func Test_Mock_Return_NotBefore_In_Order(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_Mock_Return_InOrder_Uses_NotBefore(t *testing.T) {
|
||||||
|
var mockedService = new(TestExampleImplementation)
|
||||||
|
|
||||||
|
InOrder(
|
||||||
|
mockedService.
|
||||||
|
On("TheExampleMethod", 1, 2, 3).
|
||||||
|
Return(4, nil),
|
||||||
|
mockedService.
|
||||||
|
On("TheExampleMethod2", true).
|
||||||
|
Return(),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
mockedService.TheExampleMethod(1, 2, 3)
|
||||||
|
})
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
mockedService.TheExampleMethod2(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) {
|
func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) {
|
||||||
var mockedService = new(TestExampleImplementation)
|
var mockedService = new(TestExampleImplementation)
|
||||||
|
|
||||||
|
@ -967,6 +987,35 @@ TheExampleMethod(int,int,int)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_Mock_Return_InOrder_Uses_NotBefore_Out_Of_Order(t *testing.T) {
|
||||||
|
var mockedService = new(TestExampleImplementation)
|
||||||
|
|
||||||
|
InOrder(
|
||||||
|
mockedService.
|
||||||
|
On("TheExampleMethod", 1, 2, 3).
|
||||||
|
Return(4, nil).Twice(),
|
||||||
|
mockedService.
|
||||||
|
On("TheExampleMethod2", true).
|
||||||
|
Return(),
|
||||||
|
)
|
||||||
|
|
||||||
|
expectedPanicString := `mock: Unexpected Method Call
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
TheExampleMethod2(bool)
|
||||||
|
0: true
|
||||||
|
|
||||||
|
Must not be called before:
|
||||||
|
|
||||||
|
TheExampleMethod(int,int,int)
|
||||||
|
0: 1
|
||||||
|
1: 2
|
||||||
|
2: 3`
|
||||||
|
require.PanicsWithValue(t, expectedPanicString, func() {
|
||||||
|
mockedService.TheExampleMethod2(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) {
|
func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) {
|
||||||
var mockedService = new(TestExampleImplementation)
|
var mockedService = new(TestExampleImplementation)
|
||||||
|
|
||||||
|
@ -1022,6 +1071,7 @@ func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) {
|
||||||
mockedService2.TheExampleMethod2(true)
|
mockedService2.TheExampleMethod2(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) {
|
func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
mockedService1 = new(TestExampleImplementation)
|
mockedService1 = new(TestExampleImplementation)
|
||||||
|
@ -1980,7 +2030,7 @@ func TestArgumentMatcherToPrintMismatch(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
matchingExp := regexp.MustCompile(
|
matchingExp := regexp.MustCompile(
|
||||||
`\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool`)
|
`\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool\nat: \[[^\]]+mock\/mock_test.go`)
|
||||||
assert.Regexp(t, matchingExp, r)
|
assert.Regexp(t, matchingExp, r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -1997,7 +2047,7 @@ func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
matchingExp := regexp.MustCompile(
|
matchingExp := regexp.MustCompile(
|
||||||
`\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool`)
|
`\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool\nat: \[[^\]]+mock\/mock_test.go`)
|
||||||
assert.Regexp(t, matchingExp, r)
|
assert.Regexp(t, matchingExp, r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -2028,7 +2078,7 @@ func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) {
|
||||||
func TestClosestCallFavorsFirstMock(t *testing.T) {
|
func TestClosestCallFavorsFirstMock(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+`
|
diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)`
|
||||||
matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, false, false}`, `0: \[\]bool{true, true, true}`, diffRegExp))
|
matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, false, false}`, `0: \[\]bool{true, true, true}`, diffRegExp))
|
||||||
assert.Regexp(t, matchingExp, r)
|
assert.Regexp(t, matchingExp, r)
|
||||||
}
|
}
|
||||||
|
@ -2044,7 +2094,7 @@ func TestClosestCallFavorsFirstMock(t *testing.T) {
|
||||||
func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) {
|
func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+`
|
diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)`
|
||||||
matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, true, false}`, `0: \[\]bool{false, false, false}`, diffRegExp))
|
matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, true, false}`, `0: \[\]bool{false, false, false}`, diffRegExp))
|
||||||
assert.Regexp(t, matchingExp, r)
|
assert.Regexp(t, matchingExp, r)
|
||||||
}
|
}
|
||||||
|
@ -2101,7 +2151,7 @@ func Test_isBetterMatchThanReturnsFalseIfRepeatabilityIsLessThanOrEqualToOther(t
|
||||||
|
|
||||||
func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string {
|
func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string {
|
||||||
rMethod := regexp.QuoteMeta(method)
|
rMethod := regexp.QuoteMeta(method)
|
||||||
return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+%s\s+The closest call I have is:\s+%s\s+%s\s+%s`,
|
return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+%s\s+The closest call I have is:\s+%s\s+%s\s+%s\nat: \[[^\]]+mock\/mock_test.go`,
|
||||||
rMethod, calledArg, rMethod, expectedArg, diff)
|
rMethod, calledArg, rMethod, expectedArg, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
||||||
{{.Comment}}
|
{{ replace .Comment "assert." "require."}}
|
||||||
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
|
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
|
||||||
if h, ok := t.(tHelper); ok { h.Helper() }
|
if h, ok := t.(tHelper); ok { h.Helper() }
|
||||||
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
|
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
|
||||||
|
|
|
@ -1129,6 +1129,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
|
||||||
NotContainsf(a.t, s, contains, msg, args...)
|
NotContainsf(a.t, s, contains, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true
|
||||||
|
//
|
||||||
|
// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true
|
||||||
|
func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
NotElementsMatch(a.t, listA, listB, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
|
||||||
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||||
|
// the number of appearances of each of them in both lists should not match.
|
||||||
|
// This is an inverse of ElementsMatch.
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
|
||||||
|
//
|
||||||
|
// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
|
||||||
|
func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
NotElementsMatchf(a.t, listA, listB, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||||
// a slice or a channel with len == 0.
|
// a slice or a channel with len == 0.
|
||||||
//
|
//
|
||||||
|
@ -1201,7 +1235,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
|
||||||
NotEqualf(a.t, expected, actual, msg, args...)
|
NotEqualf(a.t, expected, actual, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIs asserts that at none of the errors in err's chain matches target.
|
// NotErrorAs asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
NotErrorAs(a.t, err, target, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorAsf asserts that none of the errors in err's chain matches target,
|
||||||
|
// but if so, sets target to that error value.
|
||||||
|
func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
NotErrorAsf(a.t, err, target, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotErrorIs asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {
|
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
@ -1210,7 +1262,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface
|
||||||
NotErrorIs(a.t, err, target, msgAndArgs...)
|
NotErrorIs(a.t, err, target, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
|
// NotErrorIsf asserts that none of the errors in err's chain matches target.
|
||||||
// This is a wrapper for errors.Is.
|
// This is a wrapper for errors.Is.
|
||||||
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) {
|
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) {
|
||||||
if h, ok := a.t.(tHelper); ok {
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AssertionTesterInterface defines an interface to be used for testing assertion methods
|
// AssertionTesterInterface defines an interface to be used for testing assertion methods
|
||||||
|
@ -681,3 +683,30 @@ func TestErrorAssertionFunc(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEventuallyWithTFalse(t *testing.T) {
|
||||||
|
mockT := new(MockT)
|
||||||
|
|
||||||
|
condition := func(collect *assert.CollectT) {
|
||||||
|
True(collect, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)
|
||||||
|
True(t, mockT.Failed, "Check should fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventuallyWithTTrue(t *testing.T) {
|
||||||
|
mockT := new(MockT)
|
||||||
|
|
||||||
|
counter := 0
|
||||||
|
condition := func(collect *assert.CollectT) {
|
||||||
|
defer func() {
|
||||||
|
counter += 1
|
||||||
|
}()
|
||||||
|
True(collect, counter == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)
|
||||||
|
False(t, mockT.Failed, "Check should pass")
|
||||||
|
Equal(t, 2, counter, "Condition is expected to be called 2 times")
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
// or individual tests (depending on which interface(s) you
|
// or individual tests (depending on which interface(s) you
|
||||||
// implement).
|
// implement).
|
||||||
//
|
//
|
||||||
|
// The suite package does not support parallel tests. See [issue 934].
|
||||||
|
//
|
||||||
// A testing suite is usually built by first extending the built-in
|
// A testing suite is usually built by first extending the built-in
|
||||||
// suite functionality from suite.Suite in testify. Alternatively,
|
// suite functionality from suite.Suite in testify. Alternatively,
|
||||||
// you could reproduce that logic on your own if you wanted (you
|
// you could reproduce that logic on your own if you wanted (you
|
||||||
|
@ -63,4 +65,6 @@
|
||||||
// func TestExampleTestSuite(t *testing.T) {
|
// func TestExampleTestSuite(t *testing.T) {
|
||||||
// suite.Run(t, new(ExampleTestSuite))
|
// suite.Run(t, new(ExampleTestSuite))
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// [issue 934]: https://github.com/stretchr/testify/issues/934
|
||||||
package suite
|
package suite
|
||||||
|
|
|
@ -604,14 +604,44 @@ func TestFailfastSuite(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
)
|
)
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
|
var expect []string
|
||||||
if failFast {
|
if failFast {
|
||||||
// Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite
|
// Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite
|
||||||
assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";"))
|
expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "TearDownSuite"}
|
||||||
} else {
|
} else {
|
||||||
// Test A Fails and because we are running without failfast we continue and run Test B and then proceed to TearDownSuite
|
// Test A Fails and because we are running without failfast we continue and run Test B and then proceed to TearDownSuite
|
||||||
assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;SetupTest;Test B Passes;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";"))
|
expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "SetupTest", "Test B Passes", "TearDownTest", "TearDownSuite"}
|
||||||
}
|
}
|
||||||
|
callOrderAssert(t, expect, s.callOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tHelper interface {
|
||||||
|
Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
// callOrderAssert is a help with confirms that asserts that expect
|
||||||
|
// matches one or more times in callOrder. This makes it compatible
|
||||||
|
// with go test flag -count=X where X > 1.
|
||||||
|
func callOrderAssert(t *testing.T, expect, callOrder []string) {
|
||||||
|
var ti interface{} = t
|
||||||
|
if h, ok := ti.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
callCount := len(callOrder)
|
||||||
|
expectCount := len(expect)
|
||||||
|
if callCount > expectCount && callCount%expectCount == 0 {
|
||||||
|
// Command line flag -count=X where X > 1.
|
||||||
|
for len(callOrder) >= expectCount {
|
||||||
|
assert.Equal(t, expect, callOrder[:expectCount])
|
||||||
|
callOrder = callOrder[expectCount:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expect, callOrder)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFailfastSuiteFailFastOn(t *testing.T) {
|
func TestFailfastSuiteFailFastOn(t *testing.T) {
|
||||||
// To test this with failfast on (and isolated from other intended test failures in our test suite) we launch it in its own process
|
// To test this with failfast on (and isolated from other intended test failures in our test suite) we launch it in its own process
|
||||||
cmd := exec.Command("go", "test", "-v", "-race", "-run", "TestFailfastSuite", "-failfast")
|
cmd := exec.Command("go", "test", "-v", "-race", "-run", "TestFailfastSuite", "-failfast")
|
||||||
|
|
Loading…
Reference in New Issue