4

Maybe I am remembering wrong but I think things have changed and I missed the document. Back to basics with this question

For a class that has no methods and is just ENUMS:

public class muENUMS{ //does not even show up in test coverage not even as 0/0 lines

    public enum anExample{
        TryMe,
        YetAgain
    }

}

Salesforce says there are 0 lines to cover (in fact it never even appears in list of classes that can be covered) and during tests nothing is ever covered despite the enums being used.

Another example without enums:

public class coverageExample{ //Shows as 1 of 2 lines covered just by instantiation

    public String thisLineNotCountedOrCovered;
    public String thisLineCountedAndCovered = 'A String';
    public String thisLineCountedAndNOTCovered {get; set;}

}

So my questions in the context of just instantiating the class):

  1. Has this always been the case?

In the enum class no coverage data is calculated and the lines do not count toward to total lines to cover

In the other class it counts as 1 of 2 lines covered. The first property is not counted nor covered. The one with the getter / setter is counted and not covered.

  1. If it has not always been the case, can someone point me to the document that outlines the change? Google failed me.

It seems if a property is defaulted to a value it counts as a line of code to cover and gets covered. If a line is just a property declaration without a getter / setter it is not even considered for coverage. If it is declared with a getter / setter it is counted and if not accessed it is not covered

I believe it is important to understand this as it really impacts the access the developer gives code. Many are used to just adding getters / setters even if they are not needed. In the above that decision creates unnecessary coverage requirements.

4

Enums, interfaces, abstract methods, class-level declaration statements, class definitions, and debug statements never count for code coverage. Everything else does, including getters and setters, non-abstract methods (including the method signature), class-level assignments, and every non-debug line of code within a method always count for coverage.


Has this always been the case?

Yes, it's been this way since the inception of Apex, as far as I can remember.

In the enum class no coverage data is calculated and the lines do not count toward to total lines to cover

Yes, it is a "class-level declaration statement," and thus has no coverage.

The first property is not counted nor covered.

It is a class-level declaration statement, and thus has no coverage, either.

The one with the getter / setter is counted and not covered.

This transforms the line from a class-level declaration statement to a method signature statement, thus requiring coverage.

It seems if a property is defaulted to a value it counts as a line of code to cover and gets covered.

Yes, any time you use the = operator, it is no longer just a declaration, it is an executable statement; you're assigning a value to a variable.


Yes, there are plenty of ways to make more work for oneself, and one of them is to unnecessarily declare default getters and setters. They are strictly only necessary for Visualforce attributes, and should not be used otherwise without a reason.

I think more to the point, it is up to each developer to learn all of the particular tools in which they use (in this case, the various parts of the Apex language) to write the most optimal code possible. Unfortunately, we have a lot of poorly written resources out there that newer developers prefer to read than to check the official documentation, which further perpetuates the cycle.

This problem is further exacerbated by the fact that the documentation team, despite doing a pretty good job at what they do, presume that the readers have a decent history in Java, C#, or another related language. The "obvious parts", like why { get; set; } causes code coverage, isn't in the documentation. It's presumed that the reader knows that these are really methods, and all methods require code coverage.

  • Thank you for the time you took to answer this basic question. Believe it or not it was the first time I have had to do a class of enums..... – Eric Nov 10 '18 at 22:12
3

I do not know the answer to (1).

Regarding (2), though, the rather terse summary from Salesforce is simply this:

Code coverage percentage is a calculation of the number of covered lines divided by the sum of the number of covered lines and uncovered lines. Only executable lines of code are included.

(Emphasis mine).

Declarations of variables, classes, enums, and interfaces are considered not to be executable as such.

However, both static initialization code and instance initialization statements are executable, as are getter and setting methods, even when synthesized by the compiler. (I agree that in the case of synthesized getters and setters this makes no sense).

Here's another example illustrating this:

public class TestQ238944 {
    public enum myEnum {
        OneValue,
        Another
    }

    public myEnum initializedEnum = myEnum.OneValue;
    public String testString = 'Test';
    public Integer noValue;
    public static Integer staticVar;
    public static Integer staticWithValue = 100;
    public Integer syntheticGetSet { get; set; }
    public Integer explicitGet { 
        get { return 12; } 
    }

    static {
        staticVar = 200;
    }
}

Starting out, we obtain no coverage at all, but note that Salesforce considers all of the initialization code (static and instance) coverable, as well as the property declarations. None of it has been executed.

enter image description here

Should we touch some static property of the class, we can cause all of the static initialization code to run:

@isTest
public class TestQ238944_Test {
    @isTest
    public static void doNothing() {
        System.debug(TestQ238944.staticWithValue);
    }
}

enter image description here

The debug shows 100, as expected. The inline static initializer and the static initialization block are both run. The contrast with the first coverage snapshot shows that there is a meaningful "run" versus "not run" state for these lines. There's no difference in how inline static initialization is treated relative to static initializer blocks vis-a-vis code coverage.

As a further note on the executable nature of these lines, we can establish that they're in fact executed in a specific order: inline statics are run first, and static initializer blocks are run second. Changing the declarations like this

public static Integer staticVar;
public static Integer staticWithValue = staticVar * 2;
static {
    staticVar = 200;
}

results in a NullPointerException, but

public static Integer staticVar;
public static Integer staticWithValue = 100;
static {
    staticVar = staticWithValue * 2;
}

works fine and yields the expected result.

Next, running the following test class to actually create an instance:

@isTest
public class TestQ238944_Test {
    @isTest
    public static void doNothing() {
        TestQ238944 q = new TestQ238944();
    }
}

results in this coverage:

enter image description here

Note that the static initialization code and the instance variable initializers are shown as covered.

I think the analogy with static initialization code might be helpful here - inline instance variable assignments are to the constructor as inline static assignments are to the static block. Constructors, like static blocks, are executed after inline instance variable assignments, and both are executed after all static code completes, e.g.,

public TestQ238944() {
    noValue = staticVar * 2;
}

This is a long-winded way of saying, in essence, that these lines are coverable because they're executable, and they're executable because the Apex runtime does in fact run them just like they'd be run in some arbitrary other C/Java family language, rather than simply applying compiler magic to default variable values.

  • Thank you for the time you took to answer this basic question. Very helpful information. – Eric Nov 10 '18 at 22:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.