Blog Post

Monitoring Your Apex Code Coverage Using Salesforce DX CLI and Codecov

February 18, 2021 Karl Hughes

Code coverage is an important quality indicator for any software application, but it’s especially important to track if you’re building apps for the Salesforce AppExchange. Before deploying to Apex, your tests must pass and you must have 75 percent unit test coverage. Because code coverage is an essential part of Salesforce app development, having a code coverage monitoring tool like Codecov is a valuable piece of your CI toolbox.

In this tutorial, I’ll show you how to create code coverage reports and upload them to Codecov as part of your continuous integration process. You’ll use GitHub Actions and the Salesforce DX CLI to programmatically set up a sandbox organization in Salesforce, run your tests, and generate coverage reports. Finally, I’ll show you how you can use the Codecov dashboard to track your code coverage over time and ensure that every pull request is in compliance with the Salesforce Apex requirements.

Prerequisites

Before you get started, you should have:

Running Tests with the Salesforce DX CLI

You need some Salesforce Apex code and tests to run. If you want to start a completely new Apex project, you can follow Salesforce’s documentation here. To simplify things, I added a HelloWorld.cls, a HelloWorldTest.cls file, and the necessary meta and configuration files to this GitHub repository. You can clone this repository to your local machine if you want to get the final working code.

Note: Don’t fork this repository, as secrets won’t be included in your GitHub Action if it’s a fork.

The Apex code in the repository is a single Hello, world class and method:

public with sharing class HelloWorld {
    public String hey(String name) {
        return 'How are you ' + name + '?';
    }
}

And the test code ensures that the Apex class returns the expected message:

@isTest
public class HelloWorldTest {
    @isTest
    static void checkMessage() {
        final String name = 'Jeff';
        final String expectedMessage = 'How are you Jeff?';

        HelloWorld myHelloWorld = new HelloWorld();
        String message = myHelloWorld.hey(name);

        System.assertEquals(expectedMessage, message);
    }
}

_Note the @isTest annotation ensures that Salesforce recognizes this as a test class and disregards it from the aforementioned code coverage requirements._

After you log into the Salesforce DX CLI, you’re ready to run your tests. Create a scratch organization to run your tests on:

sfdx force:org:create --setdefaultusername --definitionfile config/project-scratch-def.json --setalias localdev --durationdays 1 -u <YOUR SALESFORCE USERNAME>

Next, push your source code to the scratch org:

sfdx force:source:push

Finally, run your tests with code coverage using the following command:

sfdx force:apex:test:run --codecoverage --resultformat human

You’ll see the results of your test run in your terminal:

=== Test Summary
NAME                 VALUE
───────────────────  ──────────────────────────────────────────────────────
Outcome              Passed
Tests Ran            1
Passing              1
Failing              0
Skipped              0
Pass Rate            100%
Fail Rate            0%
...
Test Run Coverage    100%
Org Wide Coverage    100%

You’ve successfully run your Salesforce application’s tests locally. In the following sections, you’ll see how to run these tests using a GitHub Action and push the code coverage report to Codecov.

Setting Up a GitHub Action to Run the Salesforce DX CLI

To add a GitHub Action to your repository, create a new file called code-coverage.yml to the .github/workflows directory in your project. This YAML file will contain step-by-step instructions for running your tests, but for now, just add a step to check out your latest code and install the Salesforce DX CLI using NPM:

name: code-coverage
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Install Dependencies
      run: npm install sfdx-cli

Next, you need a way to authenticate your GitHub Action with Salesforce. Depending on your security requirements, you can choose any of the authentication methods supported by Salesforce DX, but I’ll use the sfdxurl method. This uses your local CLI’s authentication to generate a Salesforce auth URL that can be used by your GitHub action to access your Salesforce environment.

Get your Salesforce auth URL by running the following command locally:

sfdx force:org:display -u <YOUR SALESFORCE USERNAME> --verbose

Save the value of the Sfdx Auth Url. You’ll need this in your GitHub Action.

Now that you have your auth URL, set up a new GitHub repository. You’ll push your completed GitHub Action here later, but for now create a new secret called SALESFORCE_AUTH_URL and set the value equal to the auth URL you just saved.

Open your code-coverage.yml file and add the following steps:

...
    - name: Populate auth file
      run: 'echo "${{ secrets.SALESFORCE_AUTH_URL }}" > ./SALESFORCE_AUTH_URL.txt'
    - name: Authenticate Dev Hub
      run: 'node_modules/sfdx-cli/bin/run force:auth:sfdxurl:store -f ./SALESFORCE_AUTH_URL.txt -a devhub -d'
    - name: Create Scratch Org
      run: node_modules/sfdx-cli/bin/run force:org:create --targetdevhubusername devhub --setdefaultusername --definitionfile config/project-scratch-def.json --setalias ciorg --durationdays 1
    - name: Deploy source
      run: node_modules/sfdx-cli/bin/run force:source:push
    - name: Run Apex tests
      run: node_modules/sfdx-cli/bin/run force:apex:test:run --codecoverage --resultformat human -d ./

This copies your SALESFORCE_AUTH_URL secret into a text file, uses it to authenticate via the Salesforce DX CLI, and then runs the same steps that you ran above locally to create a scratch org, deploy your source, and run your Apex tests.

Your GitHub Action should now work. Commit your changes and push them to your GitHub repository.

Running your Salesforce Apex tests in a GitHub Action

You’ll see your Action run through each step, ending with a (hopefully) successful test run. Now you’re ready to add Codecov to your Action.

Setting Up Codecov for Code Coverage Reporting

Assuming you’ve already linked your GitHub and Codecov accounts, the next step is to add your new GitHub repository to Codecov. Find your repo in Codecov’s list and copy the CODECOV_TOKEN from the Codecov project page. Go back to your GitHub repository and add this token as a new secret called CODECOV_TOKEN.

Next, open up your code-coverage.yml file again and add the last two steps you need to upload coverage to Codecov and delete your temporary scratch org from Salesforce:

    - name: Upload code coverage for Apex to Codecov.io
      uses: codecov/codecov-action@v2
      with:
        flags: Apex
    - name: Delete Scratch Org
      run: node_modules/sfdx-cli/bin/run force:org:delete --noprompt

Commit and push this final change and make sure the new steps work.

Uploading coverage reports to Codecov from a GitHub Action

Pull Requests Reports

Because of the Salesforce AppExchange code coverage requirements, it’s vital that your engineering team pays attention to how changes affect your coverage. Codecov’s pull request reports can show you how each pull request impacts your overall code coverage.

To demonstrate this feature, first add a blank codecov.yml file to your project. You can use this to further configure your Codecov project, but for now it just needs to exist so that pull request reports appear.

Next, create a new branch and add a second method to the HelloWorld.cls:

...
    public String bye(String name) {
        return 'Good to see you, ' + name + '.';
    }
...

This method isn’t covered by a test, so when you push the change to GitHub and create a pull request, Codecov will show you that your coverage percentage has decreased dramatically:

Pull request coverage report in GitHub

Since this is a very small pull request, it’s easy to tell which file caused the issue, but in a real-world application, you’ll probably need more information. You can click through to Codecov to see which files’ coverage has decreased so you can correct the pull request before it’s merged into production.

Codecov pull request report

The Codecov Dashboard

As your application changes and grows, you might want to know how coverage has changed over time (for better or worse). Your Codecov project page shows a line graph of coverage, which can help you make sure a new Salesforce app has the necessary coverage, or that an old project’s coverage isn’t degrading over time.

Finally, you can use Codecov’s interactive sunburst graph to see how your coverage stacks up in different parts of your Salesforce app. This can be useful if you have different teams or developers focused on different parts of the code.

Codecov sunburst graph

Before we redirect you to GitHub...
In order to use Codecov an admin must approve your org.