assert.Same() for map #1732
-
|
Original issue: #1691 Previously, Suggestions:
I’d appreciate the community’s thoughts on this behavior and potential solutions. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 10 replies
-
History
This has never been the case. There used to be a bug where Same would always pass and NotSame would always fail, regardless of what maps were compared. It happened because the error handling which checks both arguments are pointers was interpreted as an assertion result. This bug has been fixed by #1664 and Same and NotSame now correctly refuse to compare maps. Trying to assert maps are the same or not the sameNow on to the topic at hand, how should one test that maps are the same or not the same? Firstly, same is an ambiguous term. What does "same" mean for a map? Map structures onlyAn option is that either Same/NotSame or a new assertion is made to check that map values reference the same underlying map data structure or not. This would have a danger where they could be different map data structures referencing the same underlying data in their keys and/or values. One use case for this I've seen is to confirm that a map has been cloned correctly, this assertion would not test this. But as far as I know it is impossible to write this assertion in Go because you cannot get the address from a map value, even by reflection or unsafe. Please do prove me wrong with a compilable example. Map keys and valuesAnother option says that a map is the same if all of its keys and values reference the same data when they are pointers. Remember we still cannot confirm that the map data structures are the same. It's possible to confirm a true clone of a map has been made but only if the map key or value contains a pointer type. If any of those types are structs with un-exported fields then it is only possible to do this from your package and testify cannot do it for you. Here is an example: package kata_test
import (
"testing"
"github.com/stretchr/testify/assert"
)
type nest struct {
// i cannot be referenced by testify because it is unexported. It is only
// possible to write assertMapsAreTrueClones if nest containst at least one
// pointer field.
i *int
}
func TestIfy(t *testing.T) {
x, y := 1, 1
a := map[string]nest{"one": {&x}}
b := map[string]nest{"one": {&y}}
assertMapsAreTrueClones(t, a, b)
}
func assertMapsAreTrueClones(t *testing.T, a, b map[string]nest) bool {
t.Helper()
if !assert.Equalf(t, a, b, "map values are not equal\na: %v\nb: %v", a, b) {
return false
}
for k := range a {
if a[k].i == b[k].i {
t.Errorf("a[%#[1]v].i and b[%#[1]v].i are the same reference", k)
return false
}
}
return true
}If the maps' keys and values contain no pointer types and all of the keys and values have the same value; then it is not possible to confirm whether they are or are not the same map. It is only possible to say it's a different map if some of the values are different, but this would be the Equal assertion, not the Same assertion. ConclusionExcept in the case where a map's key or value has or is an exported pointer type; it is impossible for testify to assert that a map is NotSame. It is always impossible to assert that maps are the Same. If you don't agree with this, then please open a PR implementing any Same/NotSame behaviour for map values. I will either give you a testcase which proves the above, or I will happily concede. |
Beta Was this translation helpful? Give feedback.
-
|
Sorry, maybe I'm naïve and misunderstanding the problem, but what's wrong with e.g. func assertMapsAreTrueClones(t *testing.T, a, b map[string]nest) bool {
t.Helper()
if !assert.NotNil(t, a, "two nil maps cannot be the same") {
return false
}
if !assert.NotNil(t, b, "two nil maps cannot be the same") {
return false
}
if !assert.Equalf(t, fmt.Sprintf("%p", a), fmt.Sprintf("%p", b), "a and b should have same pointer") {
return false
}
return true
}? |
Beta Was this translation helpful? Give feedback.
-
|
I understand and appreciate the correction regarding the behavior of However, I still believe there is a meaningful distinction to be made between deep equality and reference equality when working with maps. While This behavior is illustrated in the following code: m := map[int]int{1: 1, 2: 2}
n := m
o := map[int]int{1: 1, 2: 2}
// Simulating Same vs Equal using pointer string formatting.
fmt.Printf("Same: %v\n", fmt.Sprintf("%p", m) == fmt.Sprintf("%p", n)) // Same: true
fmt.Printf("Equal: %v\n", fmt.Sprintf("%p", m) == fmt.Sprintf("%p", o)) // Equal: falseInstead of this kind of boilerplate, I believe it would be beneficial to have a dedicated assertion like: assert.SameMap(t, m, n)
assert.NotSameMap(t, m, o)Such a function would enhance clarity, reduce repetitive code, and express intent more directly—particularly when one needs to verify whether two variables point to the same map instance. This would align well with the principles of expressive testing and reduce the need for workarounds like comparing pointer strings. |
Beta Was this translation helpful? Give feedback.
@tturbs
Comparing map pointers
In the sense of
SameMapsdoing the pointer check a la: