Skip to content
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

Better alternative to Eventually() #902

Closed
maratori opened this issue Mar 1, 2020 · 5 comments · Fixed by #1264
Closed

Better alternative to Eventually() #902

maratori opened this issue Mar 1, 2020 · 5 comments · Fixed by #1264
Labels
assert.Eventually About assert.Eventually/EventuallyWithT enhancement pkg-assert Change related to package testify/assert

Comments

@maratori
Copy link

maratori commented Mar 1, 2020

I've found that Eventually is not really useful:

  1. I can't use assertions inside condition.
  2. Error message doesn't help to understand failure cause: Condition never satisfied.

So I use func WaitFor instead: gist.

The only disadvantage compared to Eventually:

  • WaitFor will not stop after timeout (immediately). It will call function one more time to pass error message from assertions inside that function to the test.
// WaitFor calls fn (once in tick) until it passes or timeout expired.
//
// Attempt is failed if any of following methods called in fn:
//  - Fail/FailNow
//  - Error/Errorf
//  - Fatal/Fatalf
// Otherwise attempt is considered as successful and WaitFor stops.
//
// WaitFor receives test object as the first argument.
// It is passed to fn only on the last attempt. All other attempts use fakeT.
// So the test will fail only after timeout expired.
//
// Note: t.Log() and other methods will print to stdout only on the last attempt.
func WaitFor(t TestingT, timeout time.Duration, tick time.Duration, fn func(t TestingT)) {
	timer := time.NewTimer(timeout)
	ticker := time.NewTicker(tick)
	defer timer.Stop()
	defer ticker.Stop()
	for {
		ft := &fakeT{name: t.Name()}
		didPanic := false
		func() {
			defer func() {
				if recover() != nil {
					didPanic = true
				}
			}()
			fn(ft)
		}()
		if !ft.Failed() && !didPanic {
			return
		}

		select {
		case <-timer.C:
			fn(t)
			return
		case <-ticker.C:
		}
	}
}

The rest code (with examples) can be found in this gist.

It would be great to see the same feature in testify.

@julianbrost
Copy link

I'd also like to have this.

@maratori Do you happen to have put this in some other public package in the meantime where this would be ready for use now?

@maratori
Copy link
Author

@julianbrost It is used in several repos, but they are private.

@l-abels
Copy link

l-abels commented Mar 30, 2022

I'm currently running into this same need. In particular, I have lots of tests that take this form:

PerformAnAction()

data := GetDataForValidation()

//Contains *multiple* assertions of varying types
ValidateTheData(data)

Some of these tests require polling behavior. It would be nice to be able to recover from failed assertions so I can re-run the second and third lines on a timer. Currently, I have to resort to either panic and recover or classic error return values and bypass assert entirely.

I think the most flexible solution might be to change the signature of condition to func(Assertions) bool, so that Eventually can pass a special "soft" assertion object to the condition. Failures from the soft assertion object would cause a retry, and only failures on the final pass would get turned into "hard" failures.

@l-abels
Copy link

l-abels commented Mar 30, 2022

Actually, looking at the way this library tests its own asserts by giving it a dummy testing.T:
assert := New(new(testing.T))

It shouldn't be too hard to do the same thing with a custom testing.T implementation, right? It could serve up a list of all of the inputs to its Errorf and allow us to replay them onto the real testing.T afterwards. I'll take a look at implementing that locally.

@dolmen
Copy link
Collaborator

dolmen commented Jul 31, 2023

This is now assert.EventuallyWithT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert.Eventually About assert.Eventually/EventuallyWithT enhancement pkg-assert Change related to package testify/assert
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants