From f05faff96c209b3393cb09af50ab97179b5d1075 Mon Sep 17 00:00:00 2001 From: Felipe Gasper Date: Wed, 26 Aug 2026 08:37:19 -0400 Subject: [PATCH 1/2] add must --- require/must.go | 37 ++++++++++++++++++++++++++++ require/must_forward.go | 27 +++++++++++++++++++++ require/must_forward_test.go | 47 ++++++++++++++++++++++++++++++++++++ require/must_test.go | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 require/must.go create mode 100644 require/must_forward.go create mode 100644 require/must_forward_test.go create mode 100644 require/must_test.go diff --git a/require/must.go b/require/must.go new file mode 100644 index 000000000..5bc3f173d --- /dev/null +++ b/require/must.go @@ -0,0 +1,37 @@ +//go:build go1.18 + +package require + +import ( + assert "github.com/stretchr/testify/assert" +) + +// Must calls f and returns its value, requiring that f returned a nil error. +// If f returns a non-nil error the test fails immediately, as with [NoError]. +// +// cfg := require.Must(t, func() (Config, error) { return LoadConfig(path) }) +// +// Because the test is stopped with [testing.T.FailNow], Must must be called +// from the goroutine running the test function. +func Must[T any](t TestingT, f func() (T, error), msgAndArgs ...interface{}) T { + if h, ok := t.(tHelper); ok { + h.Helper() + } + v, err := f() + if !assert.NoError(t, err, msgAndArgs...) { + t.FailNow() + } + return v +} + +// Mustf is like [Must] but uses a formatted message. +func Mustf[T any](t TestingT, f func() (T, error), msg string, args ...interface{}) T { + if h, ok := t.(tHelper); ok { + h.Helper() + } + v, err := f() + if !assert.NoErrorf(t, err, msg, args...) { + t.FailNow() + } + return v +} diff --git a/require/must_forward.go b/require/must_forward.go new file mode 100644 index 000000000..2ba849817 --- /dev/null +++ b/require/must_forward.go @@ -0,0 +1,27 @@ +//go:build go1.27 + +package require + +// Must calls f and returns its value, requiring that f returned a nil error. +// If f returns a non-nil error the test fails immediately, as with +// [Assertions.NoError]. +// +// a := require.New(t) +// cfg := a.Must(func() (Config, error) { return LoadConfig(path) }) +// +// Because the test is stopped with [testing.T.FailNow], Must must be called +// from the goroutine running the test function. +func (a *Assertions) Must[T any](f func() (T, error), msgAndArgs ...interface{}) T { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Must(a.t, f, msgAndArgs...) +} + +// Mustf is like [Assertions.Must] but uses a formatted message. +func (a *Assertions) Mustf[T any](f func() (T, error), msg string, args ...interface{}) T { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Mustf(a.t, f, msg, args...) +} diff --git a/require/must_forward_test.go b/require/must_forward_test.go new file mode 100644 index 000000000..5cdb9e200 --- /dev/null +++ b/require/must_forward_test.go @@ -0,0 +1,47 @@ +//go:build go1.27 + +package require + +import ( + "errors" + "testing" +) + +func TestMustWrapper(t *testing.T) { + mockT := new(MockT) + require := New(mockT) + + if v := require.Must(func() (int, error) { return 42, nil }); v != 42 { + t.Errorf("Must returned %d, expected 42", v) + } + if mockT.Failed { + t.Error("Must should not have failed the test") + } + + // MockT.FailNow does not stop execution, so Must returns normally here. + mockT = new(MockT) + require = New(mockT) + require.Must(func() (int, error) { return 0, errors.New("boom") }) + if !mockT.Failed { + t.Error("Must should have failed the test") + } +} + +func TestMustfWrapper(t *testing.T) { + mockT := new(MockT) + require := New(mockT) + + if v := require.Mustf(func() (string, error) { return "ok", nil }, "loading %s", "config"); v != "ok" { + t.Errorf("Mustf returned %q, expected \"ok\"", v) + } + if mockT.Failed { + t.Error("Mustf should not have failed the test") + } + + mockT = new(MockT) + require = New(mockT) + require.Mustf(func() (string, error) { return "", errors.New("boom") }, "loading %s", "config") + if !mockT.Failed { + t.Error("Mustf should have failed the test") + } +} diff --git a/require/must_test.go b/require/must_test.go new file mode 100644 index 000000000..7b76d40c1 --- /dev/null +++ b/require/must_test.go @@ -0,0 +1,43 @@ +//go:build go1.18 + +package require + +import ( + "errors" + "testing" +) + +func TestMust(t *testing.T) { + mockT := new(MockT) + + if v := Must(mockT, func() (int, error) { return 42, nil }); v != 42 { + t.Errorf("Must returned %d, expected 42", v) + } + if mockT.Failed { + t.Error("Must should not have failed the test") + } + + // MockT.FailNow does not stop execution, so Must returns normally here. + mockT = new(MockT) + Must(mockT, func() (int, error) { return 0, errors.New("boom") }) + if !mockT.Failed { + t.Error("Must should have failed the test") + } +} + +func TestMustf(t *testing.T) { + mockT := new(MockT) + + if v := Mustf(mockT, func() (string, error) { return "ok", nil }, "loading %s", "config"); v != "ok" { + t.Errorf("Mustf returned %q, expected \"ok\"", v) + } + if mockT.Failed { + t.Error("Mustf should not have failed the test") + } + + mockT = new(MockT) + Mustf(mockT, func() (string, error) { return "", errors.New("boom") }, "loading %s", "config") + if !mockT.Failed { + t.Error("Mustf should have failed the test") + } +} From 64eb06f5413a032035ed9b8d6484a57bf2ae7d8b Mon Sep 17 00:00:00 2001 From: Felipe Gasper Date: Wed, 26 Aug 2026 08:46:26 -0400 Subject: [PATCH 2/2] Try to fix CI. --- require/must.go | 6 +++++- require/must_test.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/require/must.go b/require/must.go index 5bc3f173d..859641205 100644 --- a/require/must.go +++ b/require/must.go @@ -1,4 +1,8 @@ -//go:build go1.18 +// This file uses generics, which require language version go1.18 or later. +// The go.mod go directive is go1.17 so that testify keeps building on old +// toolchains; a //go:build constraint can only raise the language version on +// Go 1.21 and later, so that is the lowest version this file can be gated on. +//go:build go1.21 package require diff --git a/require/must_test.go b/require/must_test.go index 7b76d40c1..bfba9df86 100644 --- a/require/must_test.go +++ b/require/must_test.go @@ -1,4 +1,4 @@ -//go:build go1.18 +//go:build go1.21 package require