0

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?

Why does this matter?

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
}

Coverage report: enter image description here

Coverage report when converted to if-else chain: enter image description here

6
  • Can you explain what lines in the report are not covered? You do understand that the case line itself is not greened as these are often not „executed“ just as labels are not greened even if jumped to? Commented May 30, 2024 at 13:47
  • Updated the code and also the image to show the color coding. Green = covered, Red = not covered, Grey = not tracked @Volker Commented May 30, 2024 at 13:52
  • You do understand that the case line itself is not greened as these are often not „executed“ just as labels are not greened even if jumped to? Was not aware of this. Wondering why case lines are not executed. Same thing if written as if else get covered. Commented May 30, 2024 at 13:54
  • 1
    Most case statements are like case "foo": or case 2,3,5,7,100:. Coloring them brings no extra information as it must have been "executed" (here evaluated) for the block becoming green. Commented May 30, 2024 at 14:26
  • @Volker not quite; you are correct to say that if a case block is executed, then the associated case must also have been executed. However, if the case block has not been executed, the associated case condition could have been evaluated as false or might never have been evaluated at all (i.e. is not covered). But this still doesn't matter (see my answer) Commented May 30, 2024 at 21:06

1 Answer 1

1

// 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.

Why it doesn't matter

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.

vs. else if

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:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

I have edited my question to include the coverage report when case is converted to if-else. It matters because I'm trying to run mutation testing on my code, but since the case statement is not tracked in coverage, the conditions inside it are not being mutated.
Yep, so you got the same result I saw: the 'else' is not tracked but the if condition itself does appear to be. Introducing the mutation testing concern (without clarifying whether you see the same issue in mutation testing or are just drawing a potentially incorrect conclusion from the coverage highlighting) has not clarified the question but fundamentally changed it. Your original question was answered. Having learned that, if you had a related question about how to avoid the problems this causes in mutation testing this should have been asked as a NEW question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.