Skip to content

Make suite compatible with synctest #1836

@Nocccer

Description

@Nocccer

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:

Image

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:

  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions