package gorm_test import ( "encoding/json" "log" "reflect" "testing" ) func getPreloadUser(name string) *User { return getPreparedUser(name, "Preload") } func checkUserHasPreloadData(user User, t *testing.T) { u := getPreloadUser(user.Name) if user.BillingAddress.Address1 != u.BillingAddress.Address1 { t.Error("Failed to preload user's BillingAddress") } if user.ShippingAddress.Address1 != u.ShippingAddress.Address1 { t.Error("Failed to preload user's ShippingAddress") } if user.CreditCard.Number != u.CreditCard.Number { t.Error("Failed to preload user's CreditCard") } if user.Company.Name != u.Company.Name { t.Error("Failed to preload user's Company") } if len(user.Emails) != len(u.Emails) { t.Error("Failed to preload user's Emails") } else { var found int for _, e1 := range u.Emails { for _, e2 := range user.Emails { if e1.Email == e2.Email { found++ break } } } if found != len(u.Emails) { t.Error("Failed to preload user's email details") } } } func TestPreload(t *testing.T) { user1 := getPreloadUser("user1") DB.Save(user1) preloadDB := DB.Where("role = ?", "Preload").Preload("BillingAddress").Preload("ShippingAddress"). Preload("CreditCard").Preload("Emails").Preload("Company") var user User preloadDB.Find(&user) checkUserHasPreloadData(user, t) user2 := getPreloadUser("user2") DB.Save(user2) user3 := getPreloadUser("user3") DB.Save(user3) var users []User preloadDB.Find(&users) for _, user := range users { checkUserHasPreloadData(user, t) } var users2 []*User preloadDB.Find(&users2) for _, user := range users2 { checkUserHasPreloadData(*user, t) } var users3 []*User preloadDB.Preload("Emails", "email = ?", user3.Emails[0].Email).Find(&users3) for _, user := range users3 { if user.Name == user3.Name { if len(user.Emails) != 1 { t.Errorf("should only preload one emails for user3 when with condition") } } else if len(user.Emails) != 0 { t.Errorf("should not preload any emails for other users when with condition") } } } func TestNestedPreload(t *testing.T) { log.SetFlags(log.Lshortfile) // Struct: Level3 { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{Level2: Level2{Level1: Level1{Value: "value"}}} if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2").Preload("Level2.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2s: []Level2{ { Level1s: []Level1{ {Value: "value1"}, {Value: "value2"}, }, }, { Level1s: []Level1{ {Value: "value3"}, }, }, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2s.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value1"}}, {Level1: Level1{Value: "value2"}}, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2s.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value1"}, Level1{Value: "value2"}, }, }, } if err := DB.Create(&want).Error; err != nil { panic(err) } var got Level3 if err := DB.Preload("Level2.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } // Slice: []Level3 { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{Level2: Level2{Level1: Level1{Value: "value"}}} if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{Level2: Level2{Level1: Level1{Value: "value2"}}} if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2").Preload("Level2.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2s: []Level2{ { Level1s: []Level1{ {Value: "value1"}, {Value: "value2"}, }, }, { Level1s: []Level1{ {Value: "value3"}, }, }, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2s: []Level2{ { Level1s: []Level1{ {Value: "value3"}, {Value: "value4"}, }, }, { Level1s: []Level1{ {Value: "value5"}, }, }, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2s.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1 Level1 Level3ID uint } Level3 struct { ID uint Level2s []Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value1"}}, {Level1: Level1{Value: "value2"}}, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2s: []Level2{ {Level1: Level1{Value: "value3"}}, {Level1: Level1{Value: "value4"}}, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2s.Level1").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } { type ( Level1 struct { ID uint Value string Level2ID uint } Level2 struct { ID uint Level1s []Level1 Level3ID uint } Level3 struct { ID uint Level2 Level2 } ) DB.DropTableIfExists(&Level3{}) DB.DropTableIfExists(&Level2{}) DB.DropTableIfExists(&Level1{}) if err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { panic(err) } want := make([]Level3, 2) want[0] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value1"}, Level1{Value: "value2"}, }, }, } if err := DB.Create(&want[0]).Error; err != nil { panic(err) } want[1] = Level3{ Level2: Level2{ Level1s: []Level1{ Level1{Value: "value3"}, Level1{Value: "value4"}, }, }, } if err := DB.Create(&want[1]).Error; err != nil { panic(err) } var got []Level3 if err := DB.Preload("Level2.Level1s").Find(&got).Error; err != nil { panic(err) } if !reflect.DeepEqual(got, want) { t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) } } } func toJSONString(v interface{}) []byte { r, _ := json.MarshalIndent(v, "", " ") return r }