I've been trying to learn about Go's built-in testing framework and getting proper test coverage.
In one of the files I'm testing I'm only getting ~87% coverage:
coverage: 87.5% of statements
Here's a section of the code covered in the test:
// Check that the working directory is set
if *strctFlags.PtrWorkDir == "" {
// if the working directory is empty, set it to the current directory
strTemp, err := os.Getwd()
if err != nil {
return "", errors.New("Could not get current working directory")
}
*strctFlags.PtrWorkDir = strTemp
} else if stat, err := os.Stat(*strctFlags.PtrWorkDir); err != nil ||
!stat.IsDir() {
// Existence check of the work dir
return "", errors.New("Specified directory \"" + *strctFlags.PtrWorkDir + "\" could not be found or was not a directory")
}
*strctFlags.PtrWorkDir, err = filepath.Abs(*strctFlags.PtrWorkDir)
if err != nil {
return "", errors.New("Could not determine absolute filepath: " + err.Error())
}
The parts not covered in the test according to the .out file are the "if err != nil {}" blocks, which would be errors returned from standard library calls.
While I think the likelihood of the standard library passing errors would be slim unless there would be hardware failure, I think it would be good to know that the error is handled in the application properly. Also, checking returned errors is, to my understanding, idiomatic Go so I would think it would be good to be able to test error handling properly.
How do people handle testing errors like the situations above? Is it possible to get 100% coverage, or am I doing or structuring something incorrectly? Or do people skip testing those conditions?
As @flimzy explained above it is not good to aim for 100% coverage instead aim for useful test coverage.
Though you can test the system calls with slight modification to the code like this
package foo
// Get Working directory function
var osGetWd = os.Getwd
func GetWorkingDirectory() (string,error){
strTemp, err := osGetWd() // Using the function defined above
if err != nil {
return "", errors.New("Could not get current working directory")
return strTemp,nil
}
And while testing
package foo
func TestGetWdError() {
// Mocked function for os.Getwd
myGetWd := func() (string,error) {
myErr := errors.New("Simulated error")
return "",myErr
}
// Update the var to this mocked function
osGetWd = myGetWd
// This will return error
val,err := GetWorkingDirectory()
}
This will help you to achieve 100% coverage
There are many non-hardware failure scenarios where most standard library functoins might fail. Whether you care to test those is another question. For os.Getwd()
, for instance, I might expect that call to fail if the working directory doesn't exist, (and you could go to the effort of testing this scenario).
What's probably more useful (and a better testing approach in general), would be to mock those calls, so that you can trigger errors during testing, just so that you can test your error-case code.
But please, for the love of code, don't aim for 100% test coverage. Aim for useful test coverage. It is possible to make a tool report 100% coverage without covering useful cases, and it is possible to cover useful cases without making the tool report 100%.
But true 100% coverage is a literal impossibility in most programs (even the simple "Hello World!"). So don't aim for it.