Skip to content

Commit 4b2f4d2

Browse files
tobikrisarikkfir
andauthored
add EventuallyWithT assertion (#1264)
* add EventuallyWithT assertion * Change "EventuallyWithT" condition acceptance to no-errors raised This change updates the "EventuallyWithT" assertion variants (regular, formatted, requirement) to consider a condition as "met" if no assertion errors were raised in a tick. This allows to write easier conditions which simply contain assertions, without needing to return a bool. The equivalent of a condition returning true in the previous implementation would be a a condition with a single "assert.True(..)" call. * Declare the "Collect.Copy(T)" method as a testing helper * run go generate --------- Co-authored-by: Arik Kfir <[email protected]>
1 parent b3106d7 commit 4b2f4d2

6 files changed

+290
-0
lines changed

assert/assertion_format.go

+25
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,31 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
172172
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
173173
}
174174

175+
// EventuallyWithTf asserts that given condition will be met in waitFor time,
176+
// periodically checking target function each tick. In contrast to Eventually,
177+
// it supplies a CollectT to the condition function, so that the condition
178+
// function can use the CollectT to call other assertions.
179+
// The condition is considered "met" if no errors are raised in a tick.
180+
// The supplied CollectT collects all errors from one tick (if there are any).
181+
// If the condition is not met before waitFor, the collected errors of
182+
// the last tick are copied to t.
183+
//
184+
// externalValue := false
185+
// go func() {
186+
// time.Sleep(8*time.Second)
187+
// externalValue = true
188+
// }()
189+
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
190+
// // add assertions as needed; any assertion failure will fail the current tick
191+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
192+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
193+
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
194+
if h, ok := t.(tHelper); ok {
195+
h.Helper()
196+
}
197+
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
198+
}
199+
175200
// Exactlyf asserts that two objects are equal in value and type.
176201
//
177202
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")

assert/assertion_forward.go

+50
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,56 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
322322
return Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
323323
}
324324

325+
// EventuallyWithT asserts that given condition will be met in waitFor time,
326+
// periodically checking target function each tick. In contrast to Eventually,
327+
// it supplies a CollectT to the condition function, so that the condition
328+
// function can use the CollectT to call other assertions.
329+
// The condition is considered "met" if no errors are raised in a tick.
330+
// The supplied CollectT collects all errors from one tick (if there are any).
331+
// If the condition is not met before waitFor, the collected errors of
332+
// the last tick are copied to t.
333+
//
334+
// externalValue := false
335+
// go func() {
336+
// time.Sleep(8*time.Second)
337+
// externalValue = true
338+
// }()
339+
// a.EventuallyWithT(func(c *assert.CollectT) {
340+
// // add assertions as needed; any assertion failure will fail the current tick
341+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
342+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
343+
func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
344+
if h, ok := a.t.(tHelper); ok {
345+
h.Helper()
346+
}
347+
return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...)
348+
}
349+
350+
// EventuallyWithTf asserts that given condition will be met in waitFor time,
351+
// periodically checking target function each tick. In contrast to Eventually,
352+
// it supplies a CollectT to the condition function, so that the condition
353+
// function can use the CollectT to call other assertions.
354+
// The condition is considered "met" if no errors are raised in a tick.
355+
// The supplied CollectT collects all errors from one tick (if there are any).
356+
// If the condition is not met before waitFor, the collected errors of
357+
// the last tick are copied to t.
358+
//
359+
// externalValue := false
360+
// go func() {
361+
// time.Sleep(8*time.Second)
362+
// externalValue = true
363+
// }()
364+
// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") {
365+
// // add assertions as needed; any assertion failure will fail the current tick
366+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
367+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
368+
func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
369+
if h, ok := a.t.(tHelper); ok {
370+
h.Helper()
371+
}
372+
return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...)
373+
}
374+
325375
// Eventuallyf asserts that given condition will be met in waitFor time,
326376
// periodically checking target function each tick.
327377
//

assert/assertions.go

+83
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,89 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
18271827
}
18281828
}
18291829

1830+
// CollectT implements the TestingT interface and collects all errors.
1831+
type CollectT struct {
1832+
errors []error
1833+
}
1834+
1835+
// Errorf collects the error.
1836+
func (c *CollectT) Errorf(format string, args ...interface{}) {
1837+
c.errors = append(c.errors, fmt.Errorf(format, args...))
1838+
}
1839+
1840+
// FailNow panics.
1841+
func (c *CollectT) FailNow() {
1842+
panic("Assertion failed")
1843+
}
1844+
1845+
// Reset clears the collected errors.
1846+
func (c *CollectT) Reset() {
1847+
c.errors = nil
1848+
}
1849+
1850+
// Copy copies the collected errors to the supplied t.
1851+
func (c *CollectT) Copy(t TestingT) {
1852+
if tt, ok := t.(tHelper); ok {
1853+
tt.Helper()
1854+
}
1855+
for _, err := range c.errors {
1856+
t.Errorf("%v", err)
1857+
}
1858+
}
1859+
1860+
// EventuallyWithT asserts that given condition will be met in waitFor time,
1861+
// periodically checking target function each tick. In contrast to Eventually,
1862+
// it supplies a CollectT to the condition function, so that the condition
1863+
// function can use the CollectT to call other assertions.
1864+
// The condition is considered "met" if no errors are raised in a tick.
1865+
// The supplied CollectT collects all errors from one tick (if there are any).
1866+
// If the condition is not met before waitFor, the collected errors of
1867+
// the last tick are copied to t.
1868+
//
1869+
// externalValue := false
1870+
// go func() {
1871+
// time.Sleep(8*time.Second)
1872+
// externalValue = true
1873+
// }()
1874+
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
1875+
// // add assertions as needed; any assertion failure will fail the current tick
1876+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
1877+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
1878+
func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
1879+
if h, ok := t.(tHelper); ok {
1880+
h.Helper()
1881+
}
1882+
1883+
collect := new(CollectT)
1884+
ch := make(chan bool, 1)
1885+
1886+
timer := time.NewTimer(waitFor)
1887+
defer timer.Stop()
1888+
1889+
ticker := time.NewTicker(tick)
1890+
defer ticker.Stop()
1891+
1892+
for tick := ticker.C; ; {
1893+
select {
1894+
case <-timer.C:
1895+
collect.Copy(t)
1896+
return Fail(t, "Condition never satisfied", msgAndArgs...)
1897+
case <-tick:
1898+
tick = nil
1899+
collect.Reset()
1900+
go func() {
1901+
condition(collect)
1902+
ch <- len(collect.errors) == 0
1903+
}()
1904+
case v := <-ch:
1905+
if v {
1906+
return true
1907+
}
1908+
tick = ticker.C
1909+
}
1910+
}
1911+
}
1912+
18301913
// Never asserts that the given condition doesn't satisfy in waitFor time,
18311914
// periodically checking the target function each tick.
18321915
//

assert/assertions_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,32 @@ func TestEventuallyTrue(t *testing.T) {
25332533
True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
25342534
}
25352535

2536+
func TestEventuallyWithTFalse(t *testing.T) {
2537+
mockT := new(CollectT)
2538+
2539+
condition := func(collect *CollectT) {
2540+
True(collect, false)
2541+
}
2542+
2543+
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
2544+
Len(t, mockT.errors, 2)
2545+
}
2546+
2547+
func TestEventuallyWithTTrue(t *testing.T) {
2548+
mockT := new(CollectT)
2549+
2550+
state := 0
2551+
condition := func(collect *CollectT) {
2552+
defer func() {
2553+
state += 1
2554+
}()
2555+
True(collect, state == 2)
2556+
}
2557+
2558+
True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
2559+
Len(t, mockT.errors, 0)
2560+
}
2561+
25362562
func TestNeverFalse(t *testing.T) {
25372563
condition := func() bool {
25382564
return false

require/require.go

+56
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,62 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
404404
t.FailNow()
405405
}
406406

407+
// EventuallyWithT asserts that given condition will be met in waitFor time,
408+
// periodically checking target function each tick. In contrast to Eventually,
409+
// it supplies a CollectT to the condition function, so that the condition
410+
// function can use the CollectT to call other assertions.
411+
// The condition is considered "met" if no errors are raised in a tick.
412+
// The supplied CollectT collects all errors from one tick (if there are any).
413+
// If the condition is not met before waitFor, the collected errors of
414+
// the last tick are copied to t.
415+
//
416+
// externalValue := false
417+
// go func() {
418+
// time.Sleep(8*time.Second)
419+
// externalValue = true
420+
// }()
421+
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
422+
// // add assertions as needed; any assertion failure will fail the current tick
423+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
424+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
425+
func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
426+
if h, ok := t.(tHelper); ok {
427+
h.Helper()
428+
}
429+
if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) {
430+
return
431+
}
432+
t.FailNow()
433+
}
434+
435+
// EventuallyWithTf asserts that given condition will be met in waitFor time,
436+
// periodically checking target function each tick. In contrast to Eventually,
437+
// it supplies a CollectT to the condition function, so that the condition
438+
// function can use the CollectT to call other assertions.
439+
// The condition is considered "met" if no errors are raised in a tick.
440+
// The supplied CollectT collects all errors from one tick (if there are any).
441+
// If the condition is not met before waitFor, the collected errors of
442+
// the last tick are copied to t.
443+
//
444+
// externalValue := false
445+
// go func() {
446+
// time.Sleep(8*time.Second)
447+
// externalValue = true
448+
// }()
449+
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
450+
// // add assertions as needed; any assertion failure will fail the current tick
451+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
452+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
453+
func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
454+
if h, ok := t.(tHelper); ok {
455+
h.Helper()
456+
}
457+
if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) {
458+
return
459+
}
460+
t.FailNow()
461+
}
462+
407463
// Eventuallyf asserts that given condition will be met in waitFor time,
408464
// periodically checking target function each tick.
409465
//

require/require_forward.go

+50
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,56 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
323323
Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
324324
}
325325

326+
// EventuallyWithT asserts that given condition will be met in waitFor time,
327+
// periodically checking target function each tick. In contrast to Eventually,
328+
// it supplies a CollectT to the condition function, so that the condition
329+
// function can use the CollectT to call other assertions.
330+
// The condition is considered "met" if no errors are raised in a tick.
331+
// The supplied CollectT collects all errors from one tick (if there are any).
332+
// If the condition is not met before waitFor, the collected errors of
333+
// the last tick are copied to t.
334+
//
335+
// externalValue := false
336+
// go func() {
337+
// time.Sleep(8*time.Second)
338+
// externalValue = true
339+
// }()
340+
// a.EventuallyWithT(func(c *assert.CollectT) {
341+
// // add assertions as needed; any assertion failure will fail the current tick
342+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
343+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
344+
func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
345+
if h, ok := a.t.(tHelper); ok {
346+
h.Helper()
347+
}
348+
EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...)
349+
}
350+
351+
// EventuallyWithTf asserts that given condition will be met in waitFor time,
352+
// periodically checking target function each tick. In contrast to Eventually,
353+
// it supplies a CollectT to the condition function, so that the condition
354+
// function can use the CollectT to call other assertions.
355+
// The condition is considered "met" if no errors are raised in a tick.
356+
// The supplied CollectT collects all errors from one tick (if there are any).
357+
// If the condition is not met before waitFor, the collected errors of
358+
// the last tick are copied to t.
359+
//
360+
// externalValue := false
361+
// go func() {
362+
// time.Sleep(8*time.Second)
363+
// externalValue = true
364+
// }()
365+
// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") {
366+
// // add assertions as needed; any assertion failure will fail the current tick
367+
// assert.True(c, externalValue, "expected 'externalValue' to be true")
368+
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
369+
func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
370+
if h, ok := a.t.(tHelper); ok {
371+
h.Helper()
372+
}
373+
EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...)
374+
}
375+
326376
// Eventuallyf asserts that given condition will be met in waitFor time,
327377
// periodically checking target function each tick.
328378
//

0 commit comments

Comments
 (0)