Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Unit Tests and Coverage Report in Jenkins using Jest from Create-React-App

Posted on: 2018-07-11

Since I left Microsoft Visual Studio Online (VSTS) has an employee, I have been using Jenkins which is the continuous integration (ci) platform Netflix uses. I configured two Jenkins jobs for the project I am leading. One is handling every pull request done against master and the second one is executed during the merge of any pull request into master. For many months, I didn't have the unit tests running on the platform. The reason is that I am, yet, used to how Jenkins works and even after several months feel VSTS more intuitive. Regardless, recently I took the time and setup to have my TypeScript code using Create-React-App to run my unit tests in these two Jenkins tasks. I am using Create-React-App, which come with the best testing framework I have experimented so far which is Jest. My goal was to have all the unit tests ran as well as to see the coverage.

Here are the steps required to have Jenkins handle your test. First thing is to install a dev dependency to "jest-junit". The reason is that we need to convert the format of Jest into Junit.

 npm install --save-dev jest-junit 

The next step is to download a Python script in your repository. I have mine in "tools". The reason is also about converting. Jest coverage file is not in the right format. The Python script converts the locv into Cobertura format. You can download once the script at this address.

 wget https://raw.github.com/eriwen/lcov-to-cobertura-xml/master/lcov_cobertura/lcov_cobertura.py 

Few configurations are required in the package.json. The first one is to create a test command that Jenkins execute instead of the default test command. The command calls the react-scripts. I am using TypeScript, hence I have to use the react-scripts-ts command. The next parameter is the "test" command which we still want to execute. The change starts with the test results processor. This is where you specify the jest-junit to execute once the tests are done. I set my coverage to be positioned into the "coverage" folder which is the folder I have ignored in the .gitignore and where I have normally my local coverage file outputted. Here are the three commands I have. The first one runs the test, the second run and coverage for the ci (this is the new stuff) and the last one is when I want to run locally the coverage.

 "test": "react-scripts-ts test --env=jsdom", 
 "test:ci": "react-scripts-ts test --env=jsdom --testResultsProcessor ./node_modules/jest-junit --coverage --coverageDirectory=coverage", 
 "coverage": "react-scripts-ts test --env=jsdom --coverage", 

Finally, you need few jest-unit configurations. This can be in your package.json. I have some coverage folder that I want to exclude which you can do in the jest configuration under collectCoverageFrom. I had these before doing the task we are doing of configuring Jenkins. Then, the coverage reported must be lcov and text. Finally, the new configurations are under "jest-junit". The most important configuration is the "output" which is again in the coverage folder. You can change the destination and file as you wish. However, remember the location because you will need to use the same in a few instants inside Jenkins.

 "jest": { 
   "collectCoverageFrom": [ "**/*.{ts,tsx}", "!**/node_modules/**", "!**/build/**", "!**/definitionfiles/**", "!**/WebWrokers/**", "!**/*.mock.ts", "!src/setupTests.ts" ], 
   "coverageReporters": [ "lcov", "text" ] }, 
   "jest-junit": { "suiteName": "jest tests", "output": "coverage/junit.xml", "classNameTemplate": "{classname} - {title}", "titleTemplate": "{classname} - {title}", "ancestorSeparator": " > ", "usePathForSuiteName": "true" }, 

In Jenkins, you need to add 2 build steps and 2 post-build steps. The first build step is to run the unit test with the script we just added in the package.json. The type of build step is "Execute Shell".

 npm run test:ci 

The second step is also an "Execute Shell". This one calls the python code that we placed in the "tools" folder. It is important to change the path of your lov.info and coverage.xml. Both are in my "/coverage/" folder. The "base-dir" is the directory of the source of your code.

 python tools/lcov_cobertura.py coverage/lcov.info --base-dir src/ --output coverage/coverage.xml 

The next two steps are "Post-Build". This time, two different types. The first one is "Publish JUnit test result report". It has a single parameter which is the XML file. Mine is set to "coverage/junit.xml". The second task is a "Publish Cobertura Coverage Report". It also takes a single parameter which is the coverage.xml file. Mine is set to "coverage/coverage.xml".

At that point, if you push the modification from the package.json and the Python script you will see Jenkins running the unit tests and doing the conversion. It is possible to adjust the threshold of how many tests your allow to fail to not break the build as well as setting the percentage of coverage you expect. You will get a report on the build history that allows you to sort and drill into the coverage report.