Is there an easy and compact way using Testify to assert that a slice of pointers to strings contains a pointer to a string that matches my expectation?
Imagine that you're getting a slice of pointers to strings back from a function call (maybe from an API), and you'd like to validate that it contains pointers to the strings that you'd expect. To simulate that, I'll just make a test data structure to illustrate my point:
// Shared Fixture
var one = "one"
var two = "two"
var three = "three"
var slice = []*string{&one, &two, &three}
Now I want to write a test that asserts the slice contains an expected value. I could write this test:
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, "one")
}
It doesn't work: []*string{(*string)(0x22994f0), (*string)(0x2299510), (*string)(0x2299500)} does not contain "one"
. Makes sense, the slice contains pointers to strings, and the string "one" is not one of those pointers.
I could convert it first. It takes more code, but it works:
func TestDereferencedSliceContainsString(t *testing.T) {
deref := make([]string, len(slice))
for i, v := range slice {
deref[i] = *v
}
assert.Contains(t, deref, "one")
}
I can also pass a pointer to a string as my expectation:
func TestSliceContainsPointerToExpectation(t *testing.T) {
expect := "one"
assert.Same(t, &one, &one)
assert.NotSame(t, &one, &expect)
// How can I assert that they contain values
assert.Contains(t, slice, &expect)
}
Honestly, that's not bad. I can assert that a reference to a string (pointing to a difference memory location) contains the value that I expect. The main annoyance with this path is that I can't pass a reference to a literal, which would make it take less space:
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, &"one")
}
Is there another approach that I'm not considering? Is one of these more idiomatic of golang/testify?
Yes, unfortunately the &"one"
syntax isn't valid (a few years ago, I opened an issue to allow that syntax; it was closed, though Rob Pike opened a similar issue more recently).
For now, I think the best approach is to just take the address of a variable, as in your TestSliceContainsPointerToExpectation
. Or, if you're doing this often, you can write a simple stringPtr
function so you can do it as a one-liner:
func stringPtr(value string) *string {
return &value
}
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, stringPtr("one"))
}
Or, if you're using at least Go 1.18 (with generics), you can make a generic ptr
function:
func ptr[T any](value T) *T {
return &value
}
func TestSliceContains(t *testing.T) {
assert.Contains(t, slice, ptr("one"))
}