package reflection import ( "reflect" "testing" ) type Person struct { Name string Profile Profile } type Profile struct { Age int City string } func assertContains(t testing.TB, haystack []string, needle string) { t.Helper() contains := false for _, x := range haystack { if x == needle { contains = true } } if !contains { t.Errorf("expected %v to contain %q but it didn't", haystack, needle) } } func TestWalk(t *testing.T) { cases := []struct { Name string Input interface{} ExpectedCalls []string }{ // case 0 { Name: "struct with on string field", Input: struct{ Name string }{"Drew"}, ExpectedCalls: []string{"Drew"}, }, // case 1 { "struct with two string fields", struct { Name string City string }{"Chris", "London"}, []string{"Chris", "London"}, }, // case 2 { "struct with non string field", struct { Name string Age int }{"Chris", 33}, []string{"Chris"}, }, // case 3 Nested Struct { "nested fields", Person{ "Chris", Profile{33, "London"}, }, []string{"Chris", "London"}, }, // case 4 struct as pointer { "pointers to things", &Person{ "Chris", Profile{33, "London"}, }, []string{"Chris", "London"}, }, // case 5 slice of profiles { "slices", []Profile{ {33, "London"}, {34, "Reykjavík"}, }, []string{"London", "Reykjavík"}, }, // case 6 array { "arrays", [2]Profile{ {33, "London"}, {34, "Reykjavík"}, }, []string{"London", "Reykjavík"}, }, // case 7 map THIS HAS A GOTCHA // maps in go do not guarantee order. It will eventually fake // moved to a separate test run to handle that case // { // "maps", // map[string]string{ // "Cow": "Moo", // "Sheep": "Baa", // }, // []string{"Moo", "Baa"}, // }, } for _, test := range cases { t.Run(test.Name, func(t *testing.T) { var got []string walk(test.Input, func(input string) { got = append(got, input) }) if !reflect.DeepEqual(got, test.ExpectedCalls) { t.Errorf("get %v, want %v", got, test.ExpectedCalls) } }) } t.Run("with mas", func(t *testing.T) { aMap := map[string]string{ "Cow": "Moo", "Sheep": "Baa", } var got []string walk(aMap, func(input string) { got = append(got, input) }) assertContains(t, got, "Moo") assertContains(t, got, "Baa") }) t.Run("with channels", func(t *testing.T) { aChannel := make(chan Profile) go func() { aChannel <- Profile{33, "Berlin"} aChannel <- Profile{34, "Katowice"} close(aChannel) }() var got []string want := []string{"Berlin", "Katowice"} walk(aChannel, func(input string) { got = append(got, input) }) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } }) t.Run("with function", func(t *testing.T) { aFunction := func() (Profile, Profile) { return Profile{33, "Berlin"}, Profile{34, "Katowice"} } var got []string want := []string{"Berlin", "Katowice"} walk(aFunction, func(input string) { got = append(got, input) }) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } }) }