Skip to content

proposal: testing: add T.Context #18368

@cstockton

Description

@cstockton

Both #16221, #18199 and the mailing list seems to be mostly bikeshedding in my opinion. I don't think a synchronization primitive needs to be added here, having the context is more than enough for every use case I can imagine. The context is needed to solve REAL problems with writing code, a synchronization primitive is a mere convenience that isn't useful for the problem imposed by testing. The problem is:

Write a function that must walk through pipelines of various highly concurrent systems (already use context.Context for signaling while crossing system boundaries, strengthening the need for this IMO) and may exit at any point of execution without a signal.

The only solution that makes sense to that problem is to add a signal, so lets do that please. If the change was only adjusted to cancel soon as Fatal was called, before goruntime.Goexit() it would be perfect. It removes a lot of the mental drain of writing complicated concurrent tests and allows a tighter integration between tests and your application code. As it stands now I have to make large amounts of test support structures to initialize subsystems in various ways as different tests scenarios require different subsystems to be initialized and propagate errors to the testing goroutine. This would be so much nicer if my subsystems could share tight testing integrations such as:

type SysOneTester struct {
  *testing.T
  pkg.SystemOne
}

func (..) Start(ctx context.Context) {
  if systemone.Condition() != ExpectedState { t.Fatal(`systemone has failed`) }
  // start this system, and it's dependencies
}
func TestSystemsWhenThisSystemsStateIs(t *testing.T) {
   s1, s2, s3 := SysOneTester{t, pkg.New()},SysTwoTester{t, pkg2.New()},SysFiveTester{t, pkg5.New()}
   ctx, g := errgroup.WithContext(t.Context())
   g.Go(s1)
   ...
   g.Go(s3)
   if err := g.Wait(); err != nil {
     t.Fatal(err)
   }
}

When you start multiplying having to do the above times many N, it's very difficult to integrate testing's behavior of immediately exiting when Fatal is called. It requires careful use to not leave dead goroutines running or have deadlocks all together. When I am writing unittests I am already having to think about the set of complicated systems I am testing, having to worry about writing a second application for my application to test it is counter productive.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions