3

I cannot get 100% code coverage as I cannot test Fatals in Golang.

enter image description here

I have found some Q&As including this one, but I am lost as the answers to the posts are contradictory. On the one hand it is possible to check the code coverage in Golang. On the other hand some advocate to ignore the testing of e.g. log.Fatal(err), resulting in a code coverage of less than 100%.


Attempts

As a workaround I replaced all log.Fatal by panic and thanks to this answer I am able to test all the panics and to achieve 100% code coverage.


Problem

Although I am able to get 100% code coverage I am not happy as I am basically misusing panic to get 100% code coverage. According to this answer a panic is used:

when the program, or its part, has reached an unrecoverable state

Based on this definition there are multiple snippets in my code that can throw a panic, while a log.Fatal should be used.

Community
  • 1
  • 1
030
  • 10,842
  • 12
  • 78
  • 123

2 Answers2

8

For this reason I find it useful for all functions and packages besides main to return errors rather than call log.Fatal directly. Then it can be main's decision to exit or not on errors. Trying to get 100% test coverage no matter what may give diminishing returns in a lot of cases though, so it may also be ok to call it good enough.

captncraig
  • 22,118
  • 17
  • 108
  • 151
  • I hate this return-error-up thing in go but for sync code it works. For async code however that runs on a separate `goroutine` this can go only to some extend. For async the rule is try `return` top and then `panic` and `recover`. This will satisfy the coverage god if you do it right. But yes panicking in async is counter-intuitive – downvoteit Apr 03 '20 at 14:36
4

One trick I can suggest is instead of calling log.Fatal, make your own fatal func with the same signature:

var fatal func(...interface{}) = log.Fatal

anywhere in your package. It can be unexported so other packages cannot set or use it. Then in your test you can replace it with:

fatal = func(v ...interface){
    panic(fmt.Sprint(v...))
}

or whatever you want to do.

I use this kind of trick when I need to test time.Now or something of this sort.

captncraig
  • 22,118
  • 17
  • 108
  • 151