37

We are making heavy use of stub and mock objects in our Apex tests. Most of the time we are defining the mocks as inner classes of our test classes. We have noticed, that test classes, which do contain such mock classes do show up at the Overall Code Coverage in the developer console. Such a test class does always have a code coverage of 0% and 0/zz lines covered.

I give a short example. Consider the following class of the business logic:

public virtual class Foo {

    public virtual String doSomething() {
        return anotherFunction('foo');
    }

    public virtual String anotherFunction(String value) {
        return value;
    }
}

A typical unit test would look like the following:

@isTest
private class FooTest {

    private class FooMock extends Foo {
        private Boolean anotherFunctionCalled;
        private String value;

        public override String anotherFunction(String value) {
            this.anotherFunctionCalled = true;
            this.value = value;

            return 'test';
        }
    }

    @isTest
    static void testDoSomething() {
        FooMock f = new FooMock();

        String result = f.doSomething();

        System.assertEquals('test', result);
        System.assertEquals(true, f.anotherFunctionCalled);
        System.assertEquals('foo', f.value);
    }

    @isTest
    static void testAnotherFunction() {
        Foo f = new Foo();

        String result = f.anotherFunction('blah');

        System.assertEquals('blah', result);   
    }
}

The Overall Code Coverage shows the following:

code coverage

Foo: 100% 4/4 Lines covered
FooTest: 0% 0/2 Lines covered

If you click on Apex Classes/Estimate your organization's code coverage you will get the surprising result of 66.67% (4/(2+4)) code coverage, which will not allow you to upload this code as a package.

How can we make sure, that the test classes will not be considered at the coverage estimation?

5
  • Have you tried adding @isTest to the inner class definition? May 6, 2014 at 19:00
  • @AlexTennant this results in the following compile error: Error: Compile Error: FooTest.FooMock: Only top-level non-exception class types can be marked as tests at line 5 column 19 May 6, 2014 at 19:06
  • I see your problem now, you're subclassing a non-test class. What is your reason for using this approach? May 6, 2014 at 19:18
  • With this approach we are able to control every aspect of a unit test (e.g. input parameters, calls to other functions and their parameters). Without that approach it would impractical or even impossible to test several aspects of our business logic. For more information see here. May 6, 2014 at 19:25
  • I understand the concept. What I wanted to understand was your actual use case. Techniques that work in other environments are not always the best way to go about things in Salesforce due to the limitations of the platform and its architecture. Without knowing your use case it's hard to know if that is true in this case or not. May 6, 2014 at 19:35

6 Answers 6

23

It sounds like you may have a specific problem related to mocking; however, for those experiencing the general problem stated in the question title, "Test classes are included with 0% in overall code coverage calculation", there is another cause for this issue. As @Bujji stated in his answer, it does occur (as of Spring 15) that changing a class from a non-test class to a test class can leave coverage data behind in the org that does not appear to be cleaned up by running all tests or by clearing all test data. I have just experience this issue; in a code base that I inherited, a test utility (factory) class was not marked @isTest, and so was generating coverage - incomplete coverage, in fact. I marked it @isTest, but the class didn't get removed from coverage reporting - it just dropped to 0%, lowering my overall coverage.

Instead of deleting and re-creating the class, you can clean up the old coverage data. The documentation for this known issue (resolved in Spring 15) describes a method for clearing old coverage data which resolves this issue:

1) [experience issue]

2) In the Developer console run this Tooling API query

SELECT Id, ApexClassOrTriggerId, ApexClassOrTrigger.Name,
       NumLinesCovered, NumLinesUncovered 
  FROM ApexCodeCoverageAggregate 

3) Select all results and hit delete

4) Run all tests

If you haven't seen it before, the Query tab of the Developer console has a little "use tooling api" checkbox you'll need to check.

3
  • I repeated these steps, but no luck. I deleted and recreated the classes with @isTest.
    – Avinash
    Aug 10, 2016 at 20:02
  • 2
    Helped me to estimate the code coverage correctly as of Winter'17. Thanks!
    – Artur
    Jan 18, 2017 at 13:36
  • 2) In the Developer console run this Tooling API query SELECT Id, ApexClassOrTriggerId, ApexClassOrTrigger.Name, NumLinesCovered, NumLinesUncovered FROM ApexCodeCoverageAggregate 3) Select all results and hit delete 4) Run all tests This Worked for me as well. Thnx Jason Jun 3, 2017 at 6:23
6

I'm guessing you'd want to move the FooMock definition to its own .class file. Keep only @isTest declared methods/classes in your test class file.

Or, perhaps that method needs to be declared static or global?

7
  • Why would i do that? The mock is only relevant for this particular unit test. This would severly affect the overall number of our classes in a particular project, which is approaching 1000 at the moment (still counting). May 6, 2014 at 19:01
  • Most times static is not desired, because it cannot be tested that easily. Global has to be used very carefully in the context of a commercial managed package. May 6, 2014 at 19:05
  • 2
    Thank you for your input Alexander I see that Interface classes cannot be Annotated @IsTest. ( developer.salesforce.com/page/… ) So, it appears you do need test methods to cover your mock classes as well. May 6, 2014 at 19:46
  • 2
    Yeah, you just need to access f.anotherFunction('bar') from within testDoSomething() and you'll get your two lines of coverage you're missing. May 6, 2014 at 19:55
  • @AlexanderJohannes I've encountered this too. It's rather unfortunate. Yes, they should not be top level classes and it makes a mess, but currently the metrics will be skewed unless you do. May 6, 2014 at 20:02
6

I finally managed to setup a new DevOrg with the class and test posted in the question.

Screenshot from the DevConsole:

DevConsole Code Coverage

Screenshot from the class overview:

Classes: code coverage

Screenshot from the upload as managed package:

Managed upload result

So, the described problem does not seem to have any real world impact.

3
  • superb addition to the question/answer. Thanks @Alexander Johannes May 26, 2015 at 15:07
  • Your conclusion that "does not seem to have any real world impact"... does that just refer to the fact that you were still able to deploy at 40%? Because from my POV it seems it still has a major impact of only calculating 40% coverage instead of 100%. In a small codebase it seems minor but in a large codebase it would make it pretty difficult to determine what your true coverage is at any given time. Jan 30, 2017 at 23:02
  • I can only speak for our dev-process, which is no longer affected by this issue. We are aiming and achieving 100% code coverage for each class, so that we barely need to take a look at the overall coverage number. Feb 1, 2017 at 8:23
3

I know this is a late answer but, I would like to give my two cents since I was about to ask a very similar question.

The reason your overall code coverage is so low is due to a non-test method within your test class. The only reason I know is that I had the same exact issue.

A while back our overall code coverage was including test classes with 0% code coverage. So for example, if I had a test class and a class I was covering at 100%, the overall code coverage would show up as 50%.

I went ahead and opened a case for it and it they pointed me to documentation stating that only test methods are allowed in a test class (which is weird since there is an HttpMock example that completely goes against that). I believe they changed it though since:

  1. The documentation that states this no longer exists
  2. The documentation specifies using the @isTest with a utility class as an example now.

More than likely, enough people complained to get this fixed.

Here's the other part of the story.

I was still able to publish, despite my overall code coverage not displaying properly.

Apparently, whenever a deployment occurs, they do not include any classes with the isTest annotation. So we were able to deploy to production, despite the overall code coverage saying it was too low.

0

Changing the test class from private to public resolved this for me. It no longer showed that the test class needed code coverage.

-1

I faced similar issue. I was able to fix it by adding "testMethod" keyword to the method.

Instead of this: @isTest static void testDoSomething() { }

Use this: @isTest static void testMethod testDoSomething() { }

1
  • This makes the problem even worse. If i exchange @isTest with testMethod, the unvovered lines in the test class grow from 0/2 to 0/6. May 21, 2014 at 7:24

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.