Skip to content

Replace testify assertions with custom testing helpers#159

Merged
hanzei merged 2 commits intostretchr:masterfrom
emilien-puget:remove-testify
Oct 3, 2025
Merged

Replace testify assertions with custom testing helpers#159
hanzei merged 2 commits intostretchr:masterfrom
emilien-puget:remove-testify

Conversation

@emilien-puget
Copy link
Contributor

@emilien-puget emilien-puget commented Sep 10, 2025

Summary

Replace testify assertions with custom testing helpers

Checklist

  • Tests are passing: task test
  • Code style is correct: task lint

@emilien-puget
Copy link
Contributor Author

@ccoVeille wdyt ?

Copy link

@ccoVeille ccoVeille left a comment

Choose a reason for hiding this comment

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

Great idea @emilien-puget !!

cc @dolmen @brackendawson

Some remarks anyway

Comment on lines +247 to +249
// Allow formatting helpers similar to fmt in tests using fmt.Sprintf
var _ = fmt.Sprintf
var _ = runtime.NumCPU

Choose a reason for hiding this comment

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

I don't see a need for this.

I feel like you are forcing import. So symbols are available.

Why that ?

I'm not saying you are wrong. I'm saying I'm suspicious

Comment on lines +13 to +14
var assert nonfatal
var require fatal

Choose a reason for hiding this comment

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

This idea is awesome !

I was like: WTF why removing testify imports everywhere. It won't work!

You got me, and I love the solution you found.

}
return true
}
func (a fatal) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {

Choose a reason for hiding this comment

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

There is an issue with all fatal methods.

Here I'm talking about a compatibility issue with testify that is pointless as you remove testify dependency.

But I have to mention it.

testify/require asserters don't return bool

So here (and everywhere) it should be this

Suggested change
func (a fatal) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {
func (a fatal) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) {

msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Errorf(msg)

Choose a reason for hiding this comment

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

Suggested change
t.Errorf(msg)
t.Error(msg)

msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Fatalf(msg)

Choose a reason for hiding this comment

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

Suggested change
t.Fatalf(msg)
t.Fatal(msg)

@emilien-puget
Copy link
Contributor Author

emilien-puget commented Sep 10, 2025

Great idea @emilien-puget !!

cc @dolmen @brackendawson

Some remarks anyway

thanks for the review, i made changes based on your remarks

Comment on lines +15 to +30
func (nonfatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) bool {
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Error(msg)
return false
}

func (fatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) {
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Fatal(msg)
}

Choose a reason for hiding this comment

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

Nitpicking here.

Usually t.Helper() call in on the first line of a method.

Suggested change
func (nonfatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) bool {
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Error(msg)
return false
}
func (fatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) {
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Helper()
t.Fatal(msg)
}
func (nonfatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) bool {
t.Helper()
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Error(msg)
return false
}
func (fatal) fail(t *testing.T, msg string, msgAndArgs ...interface{}) {
t.Helper()
if len(msgAndArgs) > 0 {
msg = fmt.Sprintf(msg, msgAndArgs...)
}
t.Fatal(msg)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

does it changes something or is it more an idiomatic way of doing it ?

Copy link

@ccoVeille ccoVeille Sep 10, 2025

Choose a reason for hiding this comment

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

It's clearly idiomatic, but for good reasons.

here you have calls to fmt.Sprintf which cannot call t.Error or t.Fail, so it would be OK for now.

But anyone who could come after you to update these lines, may add something that calls t.Error before the call to t.Helper a few line later. Then the line calling t.Errir would be the one reported.

Also, I thought about a possible panic, that might affect the way a test helper could react when something panic.
But I'm unsure how it would affect a test, assuming the panic is caught.

Anyway, fmt.Sprintf handles panics: an example could be a fmt.Stringer that would panic would be represented with PANIC: when passed as %s/%v to fmt.Sprintf

Do, yes. It's idiomatic and safer.

@emilien-puget
Copy link
Contributor Author

@hanzei wdyt ?

@hanzei
Copy link
Collaborator

hanzei commented Sep 23, 2025

Let me fix the CI issues first, and then I'll take a look.

Copy link
Collaborator

@hanzei hanzei left a comment

Choose a reason for hiding this comment

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

I'm not too happy that we are losing some of the nicely styled output of testify with these changes, but I see the need to move away from testify.

LGTM 👍

@hanzei
Copy link
Collaborator

hanzei commented Sep 23, 2025

@emilien-puget @ccoVeille Are we good to merge the PR?

@@ -0,0 +1,238 @@
package objx_test
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment that mentions why this file exists: this is a reimplementation of a subset of Testify's API to break a cycle dependency with Testify.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added some words about that

@hanzei hanzei merged commit b152998 into stretchr:master Oct 3, 2025
6 checks passed
@hanzei
Copy link
Collaborator

hanzei commented Oct 3, 2025

Thank you very much @emilien-puget 🎉

@emilien-puget
Copy link
Contributor Author

@hanzei could we have a release/tag so we can update it on testify side ?

@hanzei
Copy link
Collaborator

hanzei commented Oct 7, 2025

@emilien-puget Done! https://github.com/stretchr/objx/releases/tag/v0.5.3

@ccoVeille
Copy link

For record, I suggested @alexandear to use your code in @chavacava codeberg project

https://codeberg.org/chavacava/garif/pulls/15#issuecomment-10961225

He liked your idea

ccoVeille added a commit to ccoVeille/testifail that referenced this pull request Feb 24, 2026
stretchr/objx#159

Co-Authored-by: Emilien Puget <emilien-puget@users.noreply.github.com>
ccoVeille added a commit to ccoVeille/testifail that referenced this pull request Feb 24, 2026
stretchr/objx#159

Co-Authored-by: Emilien Puget <emilien-puget@users.noreply.github.com>
ccoVeille added a commit to ccoVeille/testifail that referenced this pull request Feb 24, 2026
stretchr/objx#159

Co-Authored-by: Emilien Puget <emilien-puget@users.noreply.github.com>
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.

4 participants