Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare 2 errors in Go for unit test

Tags:

go

I got a problem like below: Compare 2 errors when writing unit test

package main

import (
    "errors"
    "fmt"
    "reflect"
    
    "github.com/google/go-cmp/cmp"
    "github.com/google/go-cmp/cmp/cmpopts"
)

func main() {
    er1 := errors.New("database name is not exists")
    er2 := errors.New("database name is not exists")
    
    result1 := reflect.DeepEqual(er1, er2)
    fmt.Println("reflect: ", result1)
    
    result2 := cmp.Equal(er1, er2, cmpopts.EquateErrors())
    fmt.Println("compare: ", result2)
    
    result3 := errors.Is(er1, er2)
    fmt.Println("errorIs: ", result3)
}

And the output of the above code is:

reflect:  true
compare:  false
errorIs:  false

I want to compare 2 error and reflect.DeepEqual(er1, er2) is the 1st approach I apply and this method produce the output I want, but this approach have a warning from go lint:

avoid using reflect.DeepEqual with errorsdeepequalerrors

After google search, some people tell me some approaches that are:

  • Use cmp package to compare: cmp.Equal(er1, er2, cmpopts.EquateErrors())
  • Use errors package to compare: errors.Is(er1, er2)

But both above approaches can not produce same result like the 1st approach (use reflect.DeepEqual). How I can compare 2 errors without warning from go lint and produce the result like the reflect.DeepEqual Tks

like image 261
Tho Quach Avatar asked May 01 '26 01:05

Tho Quach


1 Answers

Depending on how you write your tests, you may depend on reflect.DeepEqual() and ignore the linter warning ;
the drawback is : you start depending on the inner structure of the errors you return.


In the testing code we write, we use one of the following patterns :

  • most of the time, we just compare the error to nil ;
  • in some cases our functions return predefined error values, and we test for these specific values :
package pkg

var ErrUnboltedGizmo = errors.New("gizmo is unbolted")

// in test functions, depending on the case :
if err == pkg.ErrUnboltedGizmo { ...
// or :
if errors.Is(err, pkg.ErrUnboltedGizmo) { ...
  • when our production code mandates that a specific error be spotted (a common use case is io.EOF), we write code that dutifully wraps that known error, and we use errors.Is() (both in production code and in testing code),
  • when the need is, in testing only, to loosely confirm that an error matches something and not something else (e.g : Parse error and not File not found), we simply search for strings in the error message :
if err == nil || !strings.Contains(err.Error(), "database name is not exists") {
    t.Errorf("unexpected error : %s", err)
}
like image 142
LeGEC Avatar answered May 03 '26 21:05

LeGEC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!