Skip to content

assert.ObjectsAreEqual: improve list/map support#1058

Open
harryzcy wants to merge 7 commits into
stretchr:masterfrom
harryzcy:issue1057
Open

assert.ObjectsAreEqual: improve list/map support#1058
harryzcy wants to merge 7 commits into
stretchr:masterfrom
harryzcy:issue1057

Conversation

@harryzcy
Copy link
Copy Markdown

@harryzcy harryzcy commented Mar 1, 2021

Summary

Function ObjectsAreEqual now compares two objects of type array/slice/map by iterating over each element. When two objects contain the same value but have different types, the function will now return true.

Changes

  • The function ObjectsAreEqualValues in assertion.go is changed.
  • Two helper functions listsAreEqualValues and mapsAreEqualValues are added.
  • Tests are added accordingly.

Motivation

Currently, assert.EqualValues would fail when two objects are of type array/slice/map and contain values of different types.

// assert would fail
assert.EqualValues(t, []interface{}{1}, []interface{}{int64(1)})

But, assert.EqualValues should always compare objects by value. So the above code should idealy pass.

Related issues

Closes #1057

Function `ObjectsAreEqual` now compares two objects of type array/slice/map by iterating over each element. When two objects contain the same value but have different types, the function will now return `true`.
@dolmen dolmen added pkg-assert Change related to package testify/assert assert.EqualValues About equality labels Oct 31, 2023
@joselitofilho
Copy link
Copy Markdown

Hi @harryzcy,

Thanks for your fix. Do you have any idea when this fix will be released?

@harryzcy
Copy link
Copy Markdown
Author

harryzcy commented Mar 4, 2024

Hi @harryzcy,

Thanks for your fix. Do you have any idea when this fix will be released?

No, I don't know when maintainers will review them.

Copy link
Copy Markdown
Collaborator

@brackendawson brackendawson left a comment

Choose a reason for hiding this comment

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

This is a similar change to #1586 and I think it can be a welcome one.

Currently the doc for EqualValues says:

EqualValues asserts that two objects are equal or convertible to the larger type and equal.

That would need to change because this behavior is quite different. Probably a second sentence describing the behavior for lists and maps. Though I've often been hesitant to have a function with different behavior based on the value of its arguments. Would it be better to have a new assertion for this? Genuine question as I'm undecided.

Apologies for the delay in this review. I advise you read my last comment first.

Comment thread assert/assertions.go
Comment on lines +221 to +222
expectedValue := reflect.ValueOf(expected)
actualValue := reflect.ValueOf(actual)
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.

These reflect calls aren't needed until after the length checking, can we move them down.

Comment thread assert/assertions.go
expectedType := expectedValue.Type()
actualType := actualValue.Type()

if expectedType.Kind() == actualType.Kind() {
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 in the spirit of EqualValues, array and slice should also pass:

assert.EqualValues(t, []interface{}{1}, [1]interface{}{int64(1)})

But this comparison prevents it.

Comment thread assert/assertions.go
return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128
}

// listsAreEqualValues gets whether two lists(arrays, slices) are equal, or if their
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.

It doesn't get if lists are equal because lists aren't comparable.

Suggested change
// listsAreEqualValues gets whether two lists(arrays, slices) are equal, or if their
// listsAreEqualValues returns true if the expected and actual lists (arrays or slices) are the same length, and each index in both lists is convertible to the larger type and equal.

Comment thread assert/assertions.go
return true
}

// mapsAreEqualValues gets whether two maps are equal, or if their
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.

As above:

Suggested change
// mapsAreEqualValues gets whether two maps are equal, or if their
// mapsAreEqualValues returns true if all the keys in the expected and actual maps are convertible to the larger type and equal.

Comment thread assert/assertions.go
// Key types should be convertible
expectedKeyType := expectedValue.Type().Key()
actualKeyType := actualValue.Type().Key()
if !expectedKeyType.ConvertibleTo(actualKeyType) {
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.

Is it wise to attempt the EqualValues algorithm on they keys as well?

I notice this fails. I'm guessing interfaces aren't convertible.

assert.EqualValues(t, map[any]any{1: 1}, map[any]any{int64(1): 1}) // false

More concerning is this case:

i := uint32(math.MaxUint32)
i++
a := map[uint32]int{
	1: 1,
	i: 2,
}
b := map[uint64]int{
	1:                  1,
	math.MaxUint32 + 1: 2,
}
assert.EqualValues(t, a, b) // false
assert.EqualValues(t, b, a) // true

The same comparison in reverse has the opposite outcome. Unless I'm mistaken you can't solve this, the key must always be a direct comparison.

@dolmen dolmen changed the title Improve list/map support for ObjectsAreEqual ObjectsAreEqual: improve list/map support May 30, 2025
@dolmen dolmen changed the title ObjectsAreEqual: improve list/map support assert.ObjectsAreEqual: improve list/map support May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

assert.EqualValues About equality bug must-rebase pkg-assert Change related to package testify/assert

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Testify assert.EqualValues should equate nested []interface{} by values

4 participants