-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
assert: early return recovering on panic in EventuallyWithT #1476
Conversation
This allows for early return from EventuallyWithT assertions, allowing for keeping the error trace
I see this PR conflicts with https://github.com/stretchr/testify/pull/1395/files, I can come with an alternative solution, passing collected errors to the func (c *CollectT) FailNow() {
panic(c.errors)
}
// then in EventuallyWithT select
case <-tick:
go func() {
defer func() {
if r := recover(); r != nil {
failed <- r
}
}()
condition(collect)
ch <- collect.errors
}()
case errs := <-failed:
for _, err := range errs {
t.Errorf("%v", err)
}
return Fail(t, "Require assertion failed", msgAndArgs...)
} |
case <-failed: | ||
collect.Copy(t) | ||
return Fail(t, "Require assertion failed", msgAndArgs...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I interpreted require
as assertions that should never happen, like network errors. So I expect require
to panic and stop the loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see assert
and require
as two different styles of writing tests with testify
. I've seen projects that forbid a use of assert
API and require
is all they use.
As of network errors they also may be temporary. Suppose we dial a 127.0.0.1:30000
but the service is starting in background. So instead of:
require.EventuallyWithT(t, func(c *assert.CollectT) {
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if assert.NoError(err) {
return
}
n, err := conn.Read(buf)
if assert.NoError(err) {
return
}
}, 100*time.Millisecond, 10*time.Millisecond)
I would want just write:
require.EventuallyWithT(t, func(c *assert.CollectT) {
conn, err := net.Dial("tcp", address)
require.NoError(err)
n, err := conn.Read(buf)
require.NoError(err)
}, 100*time.Millisecond, 10*time.Millisecond)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to use require
and assert
within the same context. I use require
when I want the unexpected condition to stop the test, so here I would expect it to panic as it does normally. If I want a test to fail and retry, I would probably use the longer
assert.EventuallyWithT(t, func(c *assert.CollectT) {
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if assert.NoError(err) {
return
}
n, err := conn.Read(buf)
if assert.NoError(err) {
return
}
}, 100*time.Millisecond, 10*time.Millisecond)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From testify docs require
calls t.FailNow
on test failure.
From testing docs t.FailNow
marks the function as having failed and stops its execution by calling runtime.Goexit (which then runs all deferred calls in the current goroutine). Execution will continue at the next test or benchmark.
so your fix is indeed the expected behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closing this PR, thank you for the review !
Summary
Catch and recover from panic on
assert.EventuallyWithT
Changes
failed
channel to handle early return failuresfailed
when recovering from panic incondition
routinecollect.FailNow
, preserving errors traceMotivation
While using
EventuallyWithT
we might want to early return on a never expected condition: in this case, we would callrequire
within thecondition
and expect to early return. Currently, callingrequire
from acondition
function panics losing the requirement error trace. Catching and recovering from thispanic
allows for early failing theEventuallyWithT
assertion and keeping the failedrequire
error trace.An example of such a use case is an assertion on a network request, where we want to early failed on http error, but we want to eventually assert on the body's properties
Try it in Golang Playground
Related issues
Closes #1396
Closes #1457