4

The switch statement in following code has a default clause that is required by the compiler and a nice safeguard, but which is never executed. After writing tests for everything else, there is no way I can (or should) test that one line. I don't care that I haven't covered that line with tests, but my TestDriven.net NCover code coverage report does show the untested line and this causes the class coverage to drop to 86%. Is there a way to let NCover exclude just this one line?

public static class OperandTypeExtensions
{
    public static string ToShortName(this OperandType type)
    {
        #region Contract
        Contract.Requires<InvalidEnumArgumentException>(Enum.IsDefined(typeof(OperandType), type));
        #endregion

        switch (type)
        {
            case OperandType.None: return "<none>";
            case OperandType.Int32: return "i32";
            case OperandType.Int64: return "i64";
            default:
                throw new NotSupportedException();
        }
    }
}

My question is similar to this question, but none of the answers help in my specific situation.

Community
  • 1
  • 1
Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157

2 Answers2

3

You can exercise it by casting integer value, which is not exists in OperandType enumeration to OperandType:

Assert.Throws<InvalidEnumArgumentException>(delegate { ((OperandType)Int32.MaxValue).ToShortName(); } );

BTW I see nothing bad in 86% coverage

UPDATE: There is no benefit of using Contract here. You will get exception anyway if value is not supported by your method.

public static class OperandTypeExtensions
{
    public static string ToShortName(this OperandType type)
    {
        switch (type)
        {
            case OperandType.None: return "<none>";
            case OperandType.Int32: return "i32";
            case OperandType.Int64: return "i64";
            default:
                throw new NotSupportedException();
        }
    }
}

And you should have defaultoption here, because if new value will be added to OperandType enum, your Contract will allow that value, but switch will not support new option.

UPDATE2: If you really need 100% coverage and Contract for this method, then use OperandType.None as default option:

public static class OperandTypeExtensions
{
    public static string ToShortName(this OperandType type)
    {
        Contract.Requires<InvalidEnumArgumentException>(Enum.IsDefined(typeof(OperandType), type));

        switch (type)
        {
            case OperandType.Int32: return "i32";
            case OperandType.Int64: return "i64";
            default:
                return "<none>";
        }
    }
}

And add to your test assertion about enum:

CollectionAssert.AreEquivalent(Enum.GetValues(typeof(OperandType)), 
                               new OperandType[] { OperandType.Int32,
                                                   OperandType.Int64, 
                                                   OperandType.None });
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • I cannot exercise it that way, as the contract will prevent me from putting any value in the method that I have not defined in the `OperandType` enumeration, which is as it should be. I'll get an `InvalidEnumArgumentException` and the line is still not covered. – Daniel A.A. Pelsmaeker May 15 '12 at 16:05
  • Exactly, I should have `default`, as a safeguard (as stated). The contract surely has a benefit: the static checker can verify that no-one ever tries to put an undefined value in the method, and it adds to the documentation of the type. (Not specifying the `Contract` constraints on the parameters is like making all parameters be of type `object`). – Daniel A.A. Pelsmaeker May 15 '12 at 18:43
  • I think it's overkill. Who will pass something different to your method, if it expects parameter of `OperandType`? – Sergey Berezovskiy May 15 '12 at 19:02
  • 1
    Maybe, I don't agree. But the point is that I want either to cover that `throw`, or ignore it, or some other creative solution. I just don't want to see it in my coverage results as it distorts the report. – Daniel A.A. Pelsmaeker May 15 '12 at 19:31
  • +1 for your second update, adding a test that checks the enum matches what you expect – Paul Phillips May 15 '12 at 22:29
-1

I also wanted to reach 100% for all my source files, not for the % but to avoid double checking every class every time I run the Code Coverage tool for false positive.

In this case and IMO, if the function is public it means you shall test it with something similar :

Assert.Throws<NotSupportedException>( OperandTypeExtensions.ToShortName() );

Other cases where it could occurs

Usually, a bigger problem occurs when the function in which the throws or the Debug.Assert is private. In that case, sometimes the lines cannot be reached by tests. Or cannot be encapsulated in a Assert.Throws.

The only way I found to ensure every line is executed. Far from ideal and very ugly, I would have prefered a comment annotation like that to disable it. Didn't work in C# though.

private string ToShortName(this OperandType type)
{
    var result = "";
    switch (type)
    {
        case OperandType.Int32: result = "i32";
        case OperandType.Int64: result = "i64";
    }
    Debug.Assert(result != "", "Invalid type.");
    return result;
}

With this solution, in your source code it will break before returning the empty string (in Debug) and Code Coverage will see the Debug.Assert line as executed.

P.S. Although, I would like to know if there is a better solution, like annotation or something to disable specifically a code block.

Community
  • 1
  • 1
ForceMagic
  • 6,230
  • 12
  • 66
  • 88