testify icon indicating copy to clipboard operation
testify copied to clipboard

Generic equality assertion

Open firelizzard18 opened this issue 2 years ago • 3 comments

I propose adding a generic equality assertion that can use a user-provided comparison function:

func Test1(t *testing.T) {
	a1 := makeA1()
	a2 := makeA2()
	EqualFunc(t, TestEqualityOfA, a1, a2)
}

func Test2(t *testing.T) {
	a1 := makeA1()
	a2 := makeA2()
	EqualFunc(t, (*A).Equal, a1, a2)
}

func EqualFunc[T any](t TestingT, fn func(a, b T) bool, expected, actual T, msgAndArgs ...interface{}) bool {
	if h, ok := t.(tHelper); ok {
		h.Helper()
	}
	if err := validateEqualArgs(expected, actual); err != nil {
		return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
			expected, actual, err), msgAndArgs...)
	}

	if !fn(expected, actual) {
		diff := diff(expected, actual)
		expected, actual := formatUnequalValues(expected, actual)
		return Fail(t, fmt.Sprintf("Not equal: \n"+
			"expected: %s\n"+
			"actual  : %s%s", expected, actual, diff), msgAndArgs...)
	}

	return true
}

It would also be nice to have one that asserted that the type has an Equal method, but that is less generally applicable. In my code base many cases would be served by Equal2 but there are cases where I have to use a func (e.g. func EqualFoo(a, b Foo) bool) instead of a method so for those cases I'd need the more general version.

func Test3(t *testing.T) {
	a1 := makeA1()
	a2 := makeA2()
	Equal2(t, a1, a2)
}

func Equal2[T interface{ Equal(T) bool }](t TestingT, a, b T, msgAndArgs ...interface{}) bool {
	return EqualFunc(t, T.Equal, a, b)
}

firelizzard18 avatar May 10 '22 18:05 firelizzard18

Please rewrite the description to show an example of how that new API would be used. This is important to decide if this would be an improvement in users' code.

dolmen avatar Jul 12 '23 14:07 dolmen

@dolmen Done. The motivation is that the project I'm working on is rife with cases where require.Equal(t, a, b) considers two objects to be unequal, but I consider them to be equal on the bases of a.Equal(b) == true. Of course I can use require.True(t, a.Equal(b)) but that provides a far less useful error message. The output testify provides if two values are unequal is much more useful.

firelizzard18 avatar Jul 13 '23 01:07 firelizzard18

Even without the user-provided equal function, it would be useful to have assert.Equal[T](TestingT, T, T) as well to avoid issues like int(10) != int32(10).

SOF3 avatar Mar 28 '24 09:03 SOF3