A complete SalesForce app pipeline #2: Automated Testing

Peter TEMPFLI
5 min readNov 3, 2019

In the previous part we have talked about security and the JWT oAuth flow; and now we are going to use it and create some automated tests on GitLab! (you can do it on any other platform as well, the ideas are the same).

What CI/CLI pipelines do

It is very simple. Once some action happens (for example, a code commit), a script starts running on the server. The good thing about GitLab is that their git repo-s come with a server, so you’re already set up!

What are we going to do

Once some new code committed into the Salesforce project repo, we are going to do the following steps:

  • Set up SFDX on the server
  • Authenticate the server to our SFDX Hub
  • Create a scratch org
  • Push code into the scratch org
  • Run tests and evaluate the results
  • Delete scratch org

This is very powerful already — imagine, every time a developer changes something, a result comes back to his or your email in 5 minutes!

How to set up the pipeline

It is super easy. Just create a .gitlab-ci.yml file in the root directory of your repo. The file structure looks like the following (don’t worry, at the end of the post I include all the code!):

The before_script part runs all the time; however, you can filter other parts by the only attribute. For example, the stage_branch part runs only when a commit happens on the stage branch. As you can see, you can put any bash code into this script.

Set up SFDX on server

The pipeline comes as an empty sandbox, so first you need to download SFDX. You need to put these lines into the before_script: we download SFDX and install.

Authenticate the server to your SFDX Hub

Once you have SFDX, you can do whatever you want with it. First, probably you want to authenticate yourself to your own DevHub. On your own computer you probably used the web-login flow, but this is not an option here. However, as we already know how to use the JWT-flow, we can authenticate ourselves without any user input! Wow! You make it like this:

sfdx force:auth:jwt:grant --clientid $SF_CONSUMER_KEY --jwtkeyfile assets/server.key --username $SF_USERNAME --setdefaultdevhubusername --setalias debHub

OK, there is a problem here: how to set the environment variable $SF_CONSUMER_KEY ? And why to set them?

It is a good practice to not put any parameters into your code — after all, you don’t want to show anyone who can see the repo your Consumer Key or Salesforce User Name! So go to GitLab Settings | CI/CD | Variables; and set them. As you can see, they are hidden, and can be protected — so only admins can access them, but they can be used on every commit.

The other issue is the server.key file. This is the key to your org — should it be in the repo? Answer: NO — please see the Appendix at the end of the post about security!

Create a scratch org

So you are connected to your DevHub. Now let’s create a scratch org and push the code into it! This way you can be sure that your code is complete, and no critical metadata is missing.

sfdx force:org:create --definitionfile config/project-scratch-def.json --setalias $SCRATCH_ORG_ALIAS --wait 10 --durationdays 1sfdx force:source:push -u $SCRATCH_ORG_ALIAS

Again, we are using environment variables.

Run tests

Once the code is pushed, you can run tests. The easiest line is this:

sfdx force:apex:test:run -u $SCRATCH_ORG_ALIAS -r human —-codecoverage

And it will give you a nice summary on the pipeline output.

But let’s do something more fancy!

- sfdx force:apex:test:run -u $SCRATCH_ORG_ALIAS -r human --codecoverage --json > test_outcome.json- cat test_outcome.json | jq ".result.coverage.coverage[] | select(.coveredPercent < $MIN_TEST_COVERAGE) | .name, .coveredPercent "- cat test_outcome.json | jq ".result.coverage.coverage[] | select(.coveredPercent < $MIN_TEST_COVERAGE) | .name" > not_covered.txt- sfdx force:org:delete -u $SCRATCH_ORG_ALIAS -p- if (( $(cat not_covered.txt | wc -l) > 0)); then exit 1; fi

What is happening here?

  • First, we run a test and write the outcome (in JSON format) to a file (test_outcome.json)
  • Than filter the file, and look for tests which has less coverage than the Minimum Test Coverage (75% lets say)
  • Than write the names of these classes to a file (not_covered.txt)
  • Delete the the scratch org — it’s good practice, otherwise your GitHub will end up having too much scratch orgs!
  • Evaluate the results (the last line): if there are any not-well-covered classes, throw an error, so the pipeline stops. Otherwise, we are good and continue.

Sum it up

As you see, with pipelines you can do whatever you want, if it is doable in terminal. You can also download and run code, so you can build very powerful testing logic. Some key points when building pipelines for SalesForce:

  • First you need to download SFDX (wget); and install it
  • Think about git-branch specific logic.
  • Use environment variables. Do not put parameters into the code.
  • Because you can get JSON output from SFDX, you can use jq for filtering it.
  • Encode keys! (see appendix)

A complete example pipeline:

A complete example pipeline with testing

Appendix: key security

It is not a good idea to upload your keys to your git repo — however, you need them in order to authenticate yourself to your orgs. What to do? Upload them encrypted!

In the first line of the script you see this:

- openssl aes-256-cbc -d -md md5 -in assets/server.key.enc -out assets/server.key -k $SERVER_KEY_PASSWORD

This lines decrypts your server.key.enc using the $SERVER_KEY_PASSWORD and creates server.key on the fly. As you know, the environment variables are safe. Without them server.key.enc is useless — this means, the integration script can run only on the server (and not by any other user who has the repo).

How to encrypt the key?

You generate the keys on your own computer and encrypt them using this line (for generating keys, see this post):

openssl aes-256-cbc -salt -e -in server.key -out server.key.enc -k <MY_PASSWORD>

Again: you should NOT upload the server.key, only server.key.enc !

--

--