Skip to content

Commit

Permalink
Merge branch 'master' into fix/equal-exported-values-accepts-everything
Browse files Browse the repository at this point in the history
  • Loading branch information
brackendawson authored Oct 23, 2024
2 parents df81388 + 75a239b commit dce9e58
Show file tree
Hide file tree
Showing 17 changed files with 707 additions and 234 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- "1.19"
- "1.20"
- "1.21"
- "1.22"
steps:
- uses: actions/checkout@v4
- name: Setup Go
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------------------------------------------------------------------------------------
> [!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.

Expand Down
4 changes: 3 additions & 1 deletion _codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ func parseTemplates() (*template.Template, *template.Template, error) {
}
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 {
return nil, nil, err
}
Expand Down
28 changes: 27 additions & 1 deletion assert/assertion_format.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 54 additions & 2 deletions assert/assertion_forward.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 74 additions & 9 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,39 @@ func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) stri
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.
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
Expand Down Expand Up @@ -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.
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
}

Expand All @@ -1916,9 +1952,10 @@ func (c *CollectT) Errorf(format string, args ...interface{}) {
c.errors = append(c.errors, fmt.Errorf(format, args...))
}

// FailNow panics.
func (*CollectT) FailNow() {
panic("Assertion failed")
// FailNow stops execution by calling runtime.Goexit.
func (c *CollectT) FailNow() {
c.fail()
runtime.Goexit()
}

// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
Expand All @@ -1931,6 +1968,16 @@ func (*CollectT) Copy(TestingT) {
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,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
Expand All @@ -1955,7 +2002,7 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
}

var lastFinishedTickErrs []error
ch := make(chan []error, 1)
ch := make(chan *CollectT, 1)

timer := time.NewTimer(waitFor)
defer timer.Stop()
Expand All @@ -1975,16 +2022,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
go func() {
collect := new(CollectT)
defer func() {
ch <- collect.errors
ch <- collect
}()
condition(collect)
}()
case errs := <-ch:
if len(errs) == 0 {
case collect := <-ch:
if !collect.failed() {
return true
}
// 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
}
}
Expand Down Expand Up @@ -2046,7 +2093,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
), 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.
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
Expand Down Expand Up @@ -2087,6 +2134,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
), 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 {
if err == nil {
return ""
Expand Down
Loading

0 comments on commit dce9e58

Please sign in to comment.