Golang: reflect.DeepEqual 返回 false

huangapple go评论115阅读模式
英文:

Golang: reflect.DeepEqual returns false

问题

我好的,以下是翻译好的代码:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "reflect"
  7. "strings"
  8. )
  9. type Result struct {
  10. Topic string `json:"topic,omitempty"`
  11. Id int `json:"id,omitempty"`
  12. }
  13. // Result represents the returned collection from a topic search.
  14. type ResultResponse struct {
  15. Result []Result `json:"results"`
  16. }
  17. func main() {
  18. want := ResultResponse{
  19. []Result{{Topic: "Clojure", Id: 1000}},
  20. }
  21. input := `{"results": [ {"topic": "Clojure", "id": 1000} ]}`
  22. p := ResultResponse{}
  23. err := json.NewDecoder(strings.NewReader(input)).Decode(&p)
  24. if err != nil {
  25. panic(err)
  26. }
  27. fmt.Println(p, want)
  28. if !reflect.DeepEqual(p, want) {
  29. log.Printf("returned %+v, want %+v", p, want)
  30. }
  31. }

希望对你有帮助!

英文:

I'm curious why this DeepEqual check is false:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "reflect"
  7. "strings"
  8. )
  9. type Result struct {
  10. Topic string `json:"topic,omitempty"`
  11. Id int `json:"id,omitempty"`
  12. }
  13. // Result represents the returned collection from a topic search.
  14. type ResultResponse struct {
  15. Result []Result `json:"results"`
  16. }
  17. func main() {
  18. want := ResultResponse{
  19. []Result{{Topic: "Clojure", Id: 1000}},
  20. }
  21. input := `{"results": [ {"topic": "Clojure", "id": 1000} ]}`
  22. p := ResultResponse{}
  23. err := json.NewDecoder(strings.NewReader(input)).Decode(&p)
  24. if err != nil {
  25. panic(err)
  26. }
  27. fmt.Println(p, want)
  28. if !reflect.DeepEqual(input, want) {
  29. log.Printf("returned %+v, want %+v", p, want)
  30. }
  31. }

答案1

得分: 8

这是我的案例:

  1. func TestGoogleAccountRepository_FindByClientCustomerIds(t *testing.T) {
  2. type args struct {
  3. ids []int
  4. }
  5. tests := []struct {
  6. name string
  7. args args
  8. want []cedar.GoogleAccount
  9. wantErr bool
  10. }{
  11. {
  12. name: "should get client customer ids correctly",
  13. args: args{ids: []int{9258066191}},
  14. want: make([]cedar.GoogleAccount, 0),
  15. wantErr: false,
  16. },
  17. }
  18. for _, tt := range tests {
  19. t.Run(tt.name, func(t *testing.T) {
  20. got, err := googleAccountRepo.FindByClientCustomerIds(tt.args.ids)
  21. if (err != nil) != tt.wantErr {
  22. t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() error = %v, wantErr %v", err, tt.wantErr)
  23. return
  24. }
  25. fmt.Printf("got = %#v, want = %#v\n", got, tt.want)
  26. if !reflect.DeepEqual(got, tt.want) {
  27. t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() = %+v, want %+v", got, tt.want)
  28. }
  29. })
  30. }
  31. }

当我首次运行这个测试时,我得到了以下消息:

  1. load env vars from local fs env file
  2. === RUN TestGoogleAccountRepository_FindByClientCustomerIds
  3. --- FAIL: TestGoogleAccountRepository_FindByClientCustomerIds (0.62s)
  4. === RUN TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly
  5. got = []cedar.GoogleAccount(nil), want = []cedar.GoogleAccount{}
  6. --- FAIL: TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly (0.62s)
  7. googleAccount_test.go:64: GoogleAccountRepository.FindByClientCustomerIds() = [], want []
  8. FAIL

注意这个消息:

  1. GoogleAccountRepository.FindByClientCustomerIds() = [], want []

看起来 gotwant 都是空的 slice,对吗?不,当我添加下面的代码后:

  1. fmt.Printf("got = %#v, want = %#v\n", got, tt.want)

它打印出:

  1. got = []cedar.GoogleAccount(nil), want = []cedar.GoogleAccount{}

正如你所看到的,got 不等于 want

这是因为我在 googleAccountRepo.FindByClientCustomerIds 方法中声明了 googleAccounts 变量,像这样:

  1. var googleAccounts []cedar.GoogleAccount

在我将其更改为

  1. var googleAccounts = make([]cedar.GoogleAccount, 0)

测试通过了:

  1. === RUN TestGoogleAccountRepository_FindByClientCustomerIds
  2. --- PASS: TestGoogleAccountRepository_FindByClientCustomerIds (0.46s)
  3. === RUN TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly
  4. got = []cedar.GoogleAccount{}, want = []cedar.GoogleAccount{}
  5. --- PASS: TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly (0.46s)
  6. PASS
  7. Process finished with exit code 0
英文:

Here is my case:

  1. func TestGoogleAccountRepository_FindByClientCustomerIds(t *testing.T) {
  2. type args struct {
  3. ids []int
  4. }
  5. tests := []struct {
  6. name string
  7. args args
  8. want []cedar.GoogleAccount
  9. wantErr bool
  10. }{
  11. {
  12. name: "should get client customer ids correctly",
  13. args: args{ids: []int{9258066191}},
  14. want: make([]cedar.GoogleAccount, 0),
  15. wantErr: false,
  16. },
  17. }
  18. for _, tt := range tests {
  19. t.Run(tt.name, func(t *testing.T) {
  20. got, err := googleAccountRepo.FindByClientCustomerIds(tt.args.ids)
  21. if (err != nil) != tt.wantErr {
  22. t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() error = %v, wantErr %v", err, tt.wantErr)
  23. return
  24. }
  25. fmt.Printf("got = %#v, want = %#v\n", got, tt.want)
  26. if !reflect.DeepEqual(got, tt.want) {
  27. t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() = %+v, want %+v", got, tt.want)
  28. }
  29. })
  30. }
  31. }

When I run this test firstly, I got below message:

  1. load env vars from local fs env file
  2. === RUN TestGoogleAccountRepository_FindByClientCustomerIds
  3. --- FAIL: TestGoogleAccountRepository_FindByClientCustomerIds (0.62s)
  4. === RUN TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly
  5. got = []cedar.GoogleAccount(nil), want = []cedar.GoogleAccount{}
  6. --- FAIL: TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly (0.62s)
  7. googleAccount_test.go:64: GoogleAccountRepository.FindByClientCustomerIds() = [], want []
  8. FAIL

Take care this message:

  1. GoogleAccountRepository.FindByClientCustomerIds() = [], want []

It seems got and want are all empty slice, right? No, after I add below code:

  1. fmt.Printf("got = %#v, want = %#v\n", got, tt.want)

It print out:

  1. got = []cedar.GoogleAccount(nil), want = []cedar.GoogleAccount{}

As you can see, got is not deep equal want.

That's because I declare the googleAccounts variable in googleAccountRepo.FindByClientCustomerIds method like this:

  1. var googleAccounts []cedar.GoogleAccount

After I change it to

  1. var googleAccounts = make([]cedar.GoogleAccount, 0)

The test pass:

  1. === RUN TestGoogleAccountRepository_FindByClientCustomerIds
  2. --- PASS: TestGoogleAccountRepository_FindByClientCustomerIds (0.46s)
  3. === RUN TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly
  4. got = []cedar.GoogleAccount{}, want = []cedar.GoogleAccount{}
  5. --- PASS: TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly (0.46s)
  6. PASS
  7. Process finished with exit code 0

答案2

得分: 2

DeepEqual检查两个条件是否相同:相同的类型和相同的值。

这里有一个棘手的情况,想象一下一个将值分配给any type(接口)的场景,例如map[string]interface{},我们大多数时候会使用数据编码器/解码器(json)处理这些值。

场景1

  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. func main() {
  7. // 场景1
  8. log.Print("场景1")
  9. m1 := make(map[string]interface{})
  10. m2 := make(map[string]interface{})
  11. m1["length"] = 1
  12. m2["length"] = 1.0
  13. if !reflect.DeepEqual(m1, m2) {
  14. log.Printf("返回了 %+v,期望是 %+v", m1, m2)
  15. }
  16. }

2009/11/10 23:00:00 返回了 map[length:1],期望是 map[length:1]

通过日志来调试问题会很困难,因为这些值看起来是相同的,除非我们检查类型(reflect.TypeOf(interface))。

场景2

  1. package main
  2. import (
  3. "encoding/json"
  4. "log"
  5. "reflect"
  6. "strings"
  7. )
  8. func main() {
  9. // 场景2
  10. log.Print("场景2")
  11. m1 := make(map[string]interface{})
  12. m2 := make(map[string]interface{})
  13. str1 := `{"length": "1"}`
  14. str2 := `{"length": 1}`
  15. json.NewDecoder(strings.NewReader(str1)).Decode(&m1)
  16. json.NewDecoder(strings.NewReader(str2)).Decode(&m2)
  17. if !reflect.DeepEqual(m1, m2) {
  18. log.Printf("返回了 %+v,期望是 %+v", m1, m2)
  19. }
  20. }

2009/11/10 23:00:00 返回了 map[length:1],期望是 map[length:1]

在这些动态值的情况下谨慎使用DeepEqual API,可能需要将其转换为单一数据类型,或者始终尝试使用相同的结构(如果可能的话)。

https://play.golang.org/p/SBZ6T5aOPQO

英文:

DeepEqual checks 2 criterias identical types, identical values,

Here is a tricky case, think about a scenarios that's values are assigning to any type (interfaces) ex: map[string]interface{} most of the time we deal with these values with data encoders/decoders (json)

Scenario 1

  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. func main() {
  7. // Scenario 1
  8. log.Print("Scenario 1")
  9. m1 := make(map[string]interface{})
  10. m2 := make(map[string]interface{})
  11. m1["length"] = 1
  12. m2["length"] = 1.0
  13. if !reflect.DeepEqual(m1, m2) {
  14. log.Printf("returned %+v, want %+v", m1, m2)
  15. }
  16. }

2009/11/10 23:00:00 returned map[length:1], want map[length:1]

With logs the issue will be hard to debug, since the values look the same unless we check the types (reflect.TypeOf(interface))

Scenario 2

  1. package main
  2. import (
  3. "encoding/json"
  4. "log"
  5. "reflect"
  6. "strings"
  7. )
  8. func main() {
  9. // Scenario 2
  10. log.Print("Scenario 2")
  11. m1 := make(map[string]interface{})
  12. m2 := make(map[string]interface{})
  13. str1 := `{"length": "1"}`
  14. str2 := `{"length": 1}`
  15. json.NewDecoder(strings.NewReader(str1)).Decode(&m1)
  16. json.NewDecoder(strings.NewReader(str2)).Decode(&m2)
  17. if !reflect.DeepEqual(m1, m2) {
  18. log.Printf("returned %+v, want %+v", m1, m2)
  19. }
  20. }

2009/11/10 23:00:00 returned map[length:1], want map[length:1]

Using DeepEqual API in these dynamic value scenarios should be done carefully, might need to convert in a single data type or always try to use the same structure if it's possible.

https://play.golang.org/p/SBZ6T5aOPQO

答案3

得分: 0

我认为这是一个编辑错误。我猜你想要编写的代码是:

  1. "reflect.DeepEqual(p, want)"

但实际上你写成了:

  1. "reflect.DeepEqual(input, want)"
英文:

I think it is an editing mistake.I guess that What you want to code is:

  1. "reflect.DeepEqual(p, want)"

but you actually wrote:

  1. "reflect.DeepEqual(input, want)"

答案4

得分: -2

DeepEqual返回false,因为你正在比较两个不可比较的类型的实例。类型ResultResponse不可比较,因为它的所有字段都不可比较。Result字段是一个切片,而根据语言规定,切片是不可比较的。

英文:

DeepEqual returns false because you're comparing two instances of a type which is not comparable. Type ResultResponse is not comparable because not all of its fields are comparable. The Result field is a slice and slices are specified by the language to be not comparable.

huangapple
  • 本文由 发表于 2015年4月3日 05:15:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/29422602.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定