Some reflective testing with gocheck
Last time I wrote about custom gocheck checkers and wrote a checker that checked if a slice of int contained a specific int – it would be nice to have a generic contains checker and using the go reflection we can write one.
Our earlier checker was built upon this function:
func contains(slice []int, value int) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
}
Go doesn't have generics so we have to use reflection to build a contains function that will work for a slice of any type. In go you define "any" type using
func contains(slice, value interface{}) bool
</pre>
If we have a slice of int and we check if it contains a type that isn't int that should return false. We will write a small function to determine if a slice or an array contains a specific type, this will be the first place we use the reflect package to examine types. Here is the code:
func containsType(slice, value interface{}) bool {
switch v := reflect.ValueOf(slice); v.Kind() {
case reflect.Slice, reflect.Array:
return v.Type().Elem() == reflect.TypeOf(value)
}
return false
}
</pre>
This function takes a slice and a value both of type
Now we can write the contains function like this:
func contains(slice, value interface{}) bool {
if containsType(slice, value) {
switch c := reflect.ValueOf(slice); c.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < c.Len(); i++ {
if reflect.DeepEqual(c.Index(i).Interface(), value) {
return true
}
}
}
}
return false
}
</pre>
This uses the containsType function and is structured in a similar fashion. The main magic is the reflect.DeepEqual function, this does a recursive equals check with reflection across arbitary structures.
We can now replace the contains function in last months code and write some tests:
func (s *S) TestContains(c *C) {
a := []int{2, 3, 4}
c.Check(a, Contains, a[0])
c.Check(a, Contains, a[1])
c.Check(a, Contains, a[2])
c.Check(a, Contains, 2)
c.Check(a, Contains, 3)
c.Check(a, Contains, 4)
c.Check(a, Not(Contains), 5)
c.Check(a, Not(Contains), a)
c.Check(a, Not(Contains), "x")
}
</pre>
