'Golang: reflect.DeepEqual returns false

I'm curious why this DeepEqual check is false:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "reflect"
    "strings"
)

type Result struct {
    Topic string `json:"topic,omitempty"`
    Id    int    `json:"id,omitempty"`
}

// Result represents the returned collection from a topic search.
type ResultResponse struct {
    Result []Result `json:"results"`
}

func main() {
    want := ResultResponse{
        []Result{{Topic: "Clojure", Id: 1000}},
    }

    input := `{"results": [ {"topic": "Clojure", "id": 1000} ]}`
    p := ResultResponse{}

    err := json.NewDecoder(strings.NewReader(input)).Decode(&p)

    if err != nil {
        panic(err)
    }

    fmt.Println(p, want)

    if !reflect.DeepEqual(input, want) {
        log.Printf("returned %+v, want %+v", p, want)
    }


}
go


Solution 1:[1]

Here is my case:

func TestGoogleAccountRepository_FindByClientCustomerIds(t *testing.T) {
    type args struct {
        ids []int
    }
    tests := []struct {
        name    string
        args    args
        want    []cedar.GoogleAccount
        wantErr bool
    }{
        {
            name:    "should get client customer ids correctly",
            args:    args{ids: []int{9258066191}},
            want:    make([]cedar.GoogleAccount, 0),
            wantErr: false,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := googleAccountRepo.FindByClientCustomerIds(tt.args.ids)
            if (err != nil) != tt.wantErr {
                t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() error = %v, wantErr %v", err, tt.wantErr)
                return
            }

            fmt.Printf("got = %#v, want = %#v\n", got, tt.want)
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("GoogleAccountRepository.FindByClientCustomerIds() = %+v, want %+v", got, tt.want)
            }
        })
    }
}

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

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

Take care this message:

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

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

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

It print out:

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:

var googleAccounts []cedar.GoogleAccount

After I change it to

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

The test pass:

=== RUN   TestGoogleAccountRepository_FindByClientCustomerIds
--- PASS: TestGoogleAccountRepository_FindByClientCustomerIds (0.46s)
=== RUN   TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly
got = []cedar.GoogleAccount{}, want = []cedar.GoogleAccount{}
    --- PASS: TestGoogleAccountRepository_FindByClientCustomerIds/should_get_client_customer_ids_correctly (0.46s)
PASS

Process finished with exit code 0

Solution 2:[2]

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

package main

import (
    "log"
    "reflect"
)

func main() {
    // Scenario 1
    log.Print("Scenario 1")
    m1 := make(map[string]interface{})
    m2 := make(map[string]interface{})

    m1["length"] = 1
    m2["length"] = 1.0
    if !reflect.DeepEqual(m1, m2) {
        log.Printf("returned %+v, want %+v", m1, m2)
    }
}

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

package main

import (
    "encoding/json"
    "log"
    "reflect"
    "strings"
)

func main() {
    // Scenario 2
    log.Print("Scenario 2")
    m1 := make(map[string]interface{})
    m2 := make(map[string]interface{})
    
    str1 := `{"length": "1"}`
    str2 := `{"length": 1}`
    
    json.NewDecoder(strings.NewReader(str1)).Decode(&m1)
    json.NewDecoder(strings.NewReader(str2)).Decode(&m2)

    if !reflect.DeepEqual(m1, m2) {
        log.Printf("returned %+v, want %+v", m1, m2)
    }
}

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

Solution 3:[3]

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

"reflect.DeepEqual(p, want)"

but you actually wrote:

"reflect.DeepEqual(input, want)"

Solution 4:[4]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 slideshowp2
Solution 2 Alexis Wilke
Solution 3 Tony Bai
Solution 4 user4744261