Skip to content

Add Must & Mustf - #1945

Open
FGasper wants to merge 2 commits into
stretchr:masterfrom
FGasper:require_must
Open

Add Must & Mustf#1945
FGasper wants to merge 2 commits into
stretchr:masterfrom
FGasper:require_must

Conversation

@FGasper

@FGasper FGasper commented Aug 26, 2026

Copy link
Copy Markdown

Summary

Convenience methods around operations that must succeed.

Changes

  • Add require.Must and require.Mustf generic functions.
  • Add corresponding require.Assertions methods.

These are usable only in Go 1.21+ and 1.27+, respectively. (The function versions would work in 1.18-1.20, but release-tag limitations in those versions prevent that.)

Motivation

These are convenience methods. Usage looks thus:

func stringOrErr() (string, error) { … }

myStr := require.Must(t, stringOrErr())
assert.Equal(t, "expected", myStr)

It’s a bit more ergonomic than:

myStr, err := stringOrErr()
require.NoError(t, err)

assert.Equal(t, "expected", myStr)

Thank you!

@brackendawson brackendawson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of new concepts to testify here.

  • Generics
  • Assertions gated by go version build constraints
  • Assertions which are only in the require package
  • It's also debatable that this isn't an assertion, it's a helper function you can use in test setup.

The precedent has been clear in the past that testify does not try to be a utility package. Is it worth breaking all this new ground for what amounts to a 1-line saving.

Also is "must" the best name? By convention it raises errors to a panic, not a test failure.

It's not a no, but these are some points around which I would need some convincing to include the change.

Comment thread require/must.go
// 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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to gate this on 1.27 too, like in require.Assertions.Must

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so, when 1.21 can run the static functions?

@brackendawson brackendawson Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be confusing to someone using 1.21-1.26 to find that it's available under require but not available under require.Assertions.

I could probably be sold on not changing this part.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add it here just for 1.27, and seeing if there’s a demand for static-only usage in prior.

Comment thread require/must.go
//
// 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 {

@brackendawson brackendawson Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mix any and interface{} in the same function declaration.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops! Missed this, sorry.

@FGasper

FGasper commented Aug 26, 2026

Copy link
Copy Markdown
Author

Thank you for your notes.

To your concerns:

  • Generics: I think these are, after several years now, a sufficiently-established pattern in Go.
  • Build-constraint gating: Is the concern here one of maintenance? There’s no danger that I see to existing codebases. Anyhow, this, too, seems well-established enough to justify itself.
  • Require-only assertions. This one I get … would an assert equivalent just return a zero value, maybe? I can’t really see the utility of assert.Must, or I’d have included it.
  • Helper function: It’s basically a helper-wrap around NoError() … ample precedent for which seems to exist.

I would argue that testify already is a utility package, insofar as it wraps testing.T’s primitives with convenience logic like NoError().

In my own experience this 1-line savings repeats over & over, impeding legibility & making tests clunkier.

To your points, though: this sort of thing may be more germane to OpenAPI’s fork.

@brackendawson

Copy link
Copy Markdown
Collaborator

I think we can probably include this.

Should we make an assert version too? I'd say no. This is permissible:

func TestIfy(t *testing.T) {
	b := bytes.Buffer{}
	count, err := b.Write(nil)
	assert.NoError(t, err)
	assert.Equal(t, count, 0)
}

But the return parameter is already occupied and almost always ignored so we just can't do it sensibly.

I actually never use the assert package myself, only require. I'm also quite supportive of the OpenAPI fork, it's close to the v2 I've always wanted to write. Any major version bump of a Go module is a fork, even if it's made in the same repo, and Stretchr is not the place to go starting a new fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants