I'm running into an issue with Golang's coverage report where case statements within a switch block are marked as not tracked, even though I have written tests that execute these cases. Is this a known issue with Go's coverage tool, or am I missing something in my tests? How can I ensure that each case statement is accurately tracked in the coverage report?
I am running mutation testing on my code (https://gremlins.dev/latest/). This tool mutates the lines of code that are covered by the existing unit tests. Now, the conditions in the case statement can easily be mutated e.g. len(mobile) < 10
can be mutated to len(mobile) > 10
. But since the line is not tracked, it is not considered for mutation. And hence, the concern.
Code:
func SanitizeMobile(mobile string) (string, serror.SError) { // Not tracked
// Remove all non-digit characters // Covered
var cleanedMobile strings.Builder // Covered
for _, char := range mobile { // Covered
if unicode.IsDigit(char) { // Covered
cleanedMobile.WriteRune(char) // Covered
} // Covered
} // Covered
mobile = cleanedMobile.String() // Covered
switch { // Covered
case len(mobile) < 10 || len(mobile) > 12: // Not tracked
// After cleaning up all non-digits, mobile number cannot be greater than 12 digits in length // Covered
return "", serror.New(serror.BadRequestError, InvalidMobile, nil) // Covered
case len(mobile) == 10 && mobile[0] == '0': // Not tracked
// In case mobile number is 10 digits long, we don't allow it to start with 0 // Covered
// As of now, first digit of the mobile number can only be 5,6,7,8,9 but we relax // Covered
// this criteria a bit, to allow 1,2,3,4 as well. // Covered
return "", serror.New(serror.BadRequestError, InvalidMobile, nil) // Covered
case len(mobile) == 11 && mobile[0] != '0': // Not tracked
return "", serror.New(serror.BadRequestError, InvalidMobile, nil) // Covered
case len(mobile) == 12 && mobile[0:2] != "91": // Not tracked
return "", serror.New(serror.BadRequestError, InvalidMobile, nil) // Covered
default: // Not tracked
break // Covered
} // Not tracked
return mobile[len(mobile)-constant.IndianMobileLen:], nil // Covered
}
// Not tracked
indicates that coverage is not considered for that line. i.e. covered or not covered makes no difference
If a case
statement block is executed, then the case condition itself must have been evaluated as true
.
If a case
statement block is not executed, it may be because either the case
condition evaluated false or was never evaluated. This might seem like an oversight, causing potentially uncovered code to be missed in the coverage report (hence your concern, I presume).
However, the fact that the associated case
block is itself covered makes this irrelevant.
If a case
block is not executed, then this will be reflected in the coverage report as uncovered code regardless of whether it was not executed due to the associated case
condition evaluating false
or not being evaluated at all.
To cover any uncovered case
block, a test suite must ensure that the associated case
condition evaluates true
in at least one test case. In order to achieve that, the case
condition must be reachable, dealing with both possible reasons for an uncovered case
block.
I would be curious to see an example of similar observations involving else if
statements.
In a quick test (using Golang 1.22.2), I could not create a scenario where the condition in an else if
was not tracked. However, the else
clause in the statement (specifically) does appear to be not tracked: