technology from back to front

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:

<pre>
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

interface{}
, the empty interface. So we would like to build a function with this signature:

<pre>
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:

<pre>
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

interface{}
so they can hold any type. The switch and case statement check if the parameter advertised as a slice is actually a slice, or an array. If that is true it compares the type held by the slice with the type of the value. The real work here is carried out by the reflect.Value struct returned by the reflect.ValueOf function. The reflect.Value struct contains parameters which describe any go type. In this function, Kind() allows us to determine that we have been given an array or a struct, and Type().Elem() allows us to determine what type the slice contains.

Now we can write the contains function like this:

<pre>
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:

<pre>
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>
by
tim
on
30/09/11
 
 
2000-13 LShift Ltd, 1st Floor Office, Hoxton Point, 6 Rufus Street, London, N1 6PE, UK +44 (0)20 7729 7060