Description
The new introduced synctest package with go 1.25 is pretty handy for asynchronous testing. The problem is that the testify suite is not yet ready to make proper use of it. To use synctest all timer, channels, ... need to be created inside the bubble (inside synctest.Test). If you want to reuse channels or timers of a suite, this is currently not possible, because the SetupTest function for example is executed before we can call synctest.Test inside a suite test function.
Example:
package sync_test
import (
"fmt"
"testing"
"testing/synctest"
"time"
"github.com/stretchr/testify/suite"
)
func TestSync(t *testing.T) {
suite.Run(t, new(TestSuiteSync))
}
type TestSuiteSync struct {
suite.Suite
ticker *time.Ticker
}
func (s *TestSuiteSync) SetupTest() {
s.ticker = time.NewTicker(time.Second)
}
func (s *TestSuiteSync) TestWithSyncTestDoesNotWork() {
synctest.Test(s.T(), func(t *testing.T) {
<-s.ticker.C
})
}
func (s *TestSuiteSync) TestWithSyncTestDoesWork() {
synctest.Test(s.T(), func(t *testing.T) {
s.SetupTest() // call setup again to create ticker in bubble
<-s.ticker.C
})
}
Output:
Proposed solution
My solution for now would be to introduce a new pattern to discover which tests in a suite shall be run with synctest.Test instead t.Run. Currently every method of the suite that starts with Test* is considerd as a synchronous test and will be run with t.Run. What if we introduce SyncTest* as prefix for suite methods that will be run with synctest.Test? I would not use TestSync* because this could break already written tests of testify users.
For subtest a new method s.RunSync could be provided to run subtests in a bubble. This should only be allowed in synchronous test methods (Test*), because it is not allowed to run a synctest bubble in another synctest bubble.
Open Problems to solve:
- How to make sure
SetupSuite is also called inside the bubble or how to avoid setup channels and timers inside SetupSuite
Example:
package sync_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
)
func TestTicker(t *testing.T) {
suite.Run(t, new(TestSuiteTicker))
}
type TestSuiteTicker struct {
suite.Suite
ticker *time.Ticker
}
func (s *TestSuiteTicker) SetupTest() {
s.ticker = time.NewTicker(time.Second)
}
// t.Run()
// s.SetupTest()
func (s *TestSuiteTicker) TestRealClock() {
<-s.ticker.C
// takes 1s
}
// synctest.Test()
// s.SetupTest()
func (s *TestSuiteTicker) SyncTestFakeClock() {
<-s.ticker.C
// takes ~0s
}
Use case
Suites where you want to test synchronous and asynchronous.
Description
The new introduced synctest package with go 1.25 is pretty handy for asynchronous testing. The problem is that the testify suite is not yet ready to make proper use of it. To use synctest all timer, channels, ... need to be created inside the bubble (inside
synctest.Test). If you want to reuse channels or timers of a suite, this is currently not possible, because theSetupTestfunction for example is executed before we can callsynctest.Testinside a suite test function.Example:
Output:
Proposed solution
My solution for now would be to introduce a new pattern to discover which tests in a suite shall be run with
synctest.Testinsteadt.Run. Currently every method of the suite that starts withTest*is considerd as a synchronous test and will be run witht.Run. What if we introduceSyncTest*as prefix for suite methods that will be run withsynctest.Test? I would not useTestSync*because this could break already written tests of testify users.For subtest a new method
s.RunSynccould be provided to run subtests in a bubble. This should only be allowed in synchronous test methods (Test*), because it is not allowed to run a synctest bubble in another synctest bubble.Open Problems to solve:
SetupSuiteis also called inside the bubble or how to avoid setup channels and timers insideSetupSuiteExample:
Use case
Suites where you want to test synchronous and asynchronous.