The UpgradeJS Blog

Set up a Local SonarQube Instance for OSS - Chapter 2

SonarQube opens a new window is an open-source platform developed by SonarSource opens a new window that provides continuous inspection of code quality through static code analysis.

There are two ways to set up SonarQube analysis - you can select from the cloud solution (sonarcloud opens a new window ) or install a SonarQube instance on your machine or a remote server. In the first chapter opens a new window we set up a local SonarQube instance and ran the analysis for a simple JavaScript application

In this chapter, we will continue to work with the same application and add more features to it.

The stack

JavaScript, TypeScript, Cypress, Jest, nyc, cypress-sonarqube-reporter, jest-sonar.

The scope

In this chapter, we will highlight the following features and how to set them up:

  • Executing tests and creating SonarQube reports with the help of Jest and Cypress
  • Including test coverage information in the SonarQube report
  • Incorporating test case numbers in the SonarQube report

Jest and Cypress configurations

To show the test coverage statistics in the SonarQube report, we need to generate the coverage data first. We will use Jest and Cypress for that.

So, we will use Jest to cover our application with unit tests and Cypress to cover it with end-to-end tests. With some configuration, these tools can provide you with coverage data.

Jest configuration

To generate the coverage report for Jest we need to add the following configuration to the jest.config.js file:

import type { Config } from 'jest'

const config: Config = {
  /* This option tells Jest to collect the coverage data from our tests */
  collectCoverage: true,
  /* This option specifies where the coverage report will be saved */
  coverageDirectory: "coverage/jest"
}

export default config

Cypress configuration

To generate the coverage report for Cypress we need to add the following configuration to the cypress.config.js file:

import { defineConfig } from 'cypress'

export default defineConfig({
  env: {
    codeCoverage: {
      exclude: ['cypress/**/*.*', 'node_modules/**/*.*']
    }
  },
  e2e: {
    /* Application url */
    baseUrl: 'http://localhost:3000',
    setupNodeEvents(on, config) {
      /* This task is required to collect the coverage data, more - https://docs.cypress.io/guides/tooling/code-coverage */
      require('@cypress/code-coverage/task')(on, config);

      return config;
    }
  }
})

Optionally, you can specify the folder in which the Cypress coverage report will be generated. By default, it is coverage. The tool that generates reports for Cypress is called nyc opens a new window , and to configure it, you can use the .nycrc file with the following configuration:

{
  "report-dir": "coverage/cypress"
}

Also, you need to instruct your application code to collect the coverage data, in this chapter we will not cover this topic. But you can find more information about it in the Cypress documentation opens a new window or in our blog post - Combining Code Coverage Data From Multiple Testing Tools opens a new window .

SonarQube configuration

From the first chapter opens a new window you might be familiar with the sonar-project.properties file.

It is a configuration file for SonarQube. It contains the project metadata and the configuration for the analysis. In this chapter, we will add the test-related configurations to it.

Test coverage in the report

To show the test coverage in the SonarQube report, we need to add the following configuration to the sonar-project.properties file:

# Specifying the directories where the tests live
sonar.tests=__tests__,cypress/e2e

# Specifying the files containing code coverage
sonar.javascript.lcov.reportPaths=coverage/jest/lcov.info,coverage/cypress/lcov.info

In addition, you can fine-tune what you want or don’t want to be part of the scope of your scan. There are two properties which can help you with this - inclusions and exclusions.

An example of usage:

sonar.tests.inclusions=__tests__/**/*.test.ts,cypress/e2e/*.cy.ts

This configuration should be enough to see the test coverage in the report:

SonarQube report page showing 100% test coverage

But, as you can see in this screenshot, in the place where the test cases are supposed to be counted there is a dash mark instead. Let’s fix it.

Test case count in the report

This feature sounds pretty straightforward. But it is not. The reason for that is that SonarQube doesn’t know how to count the test cases. It is up to us to provide the tool with the information about the test cases count.

To do that, we need to add some configuration details to our jest.config.js, cypress.config.js, and to the sonar-project.properties files.

Let’s start with the jest.config.js file. We need to add the following configuration to it:

{
  reporters: [
    "default",
    [
      "jest-sonar",
      {
        outputDirectory: "coverage/jest",
        outputName: "sonarqube-report.xml"
      }
    ]
  ]
}

In addition to the default reporter, we added the jest-sonar opens a new window reporter. This reporter will generate the sonarqube-report.xml file in the coverage/jest directory. This file will contain information about all test cases that were run.

The coverage/jest/sonarqube-report.xml file will look like this:

<testExecutions version="1">
  <file path="__tests__/home.test.tsx">
    <testCase name="Home renders link to about page" duration="40" />
    <testCase name="Home page should display tested by jest" duration="8" />
    <testCase name="Home should test untested path" duration="5" />
    <testCase name="Home renders homepage unchanged" duration="3" />
  </file>
</testExecutions>

Now, we need to add the following configuration to the cypress.config.js file:

{
  reporter: 'cypress-sonarqube-reporter',
  reporterOptions: {
    outputDir: 'coverage/cypress/reports',
    mergeOutputDir: 'coverage/cypress',
    mergeFileName: 'sonarqube-report.xml',
    preserveSpecsDir: false
  },
  e2e: {
    baseUrl,
    setupNodeEvents(on, config) {
      /* Rest of the previous configuration. */
      on('after:run', (results) => {
        return require('cypress-sonarqube-reporter/mergeReports')(results);
      });
      return config;
    }
  }
}

In this configuration, we included the cypress-sonarqube-reporter opens a new window reporter, which generates a sonarqube-report.xml file in the coverage/cypress/reports directory.

This file contains information about all executed test cases. To consolidate all reports from the coverage/cypress/reports directory into a single file, we added an after:run event listener.

This listener merges all reports into a single coverage/cypress/sonarqube-report.xml file, this is particularly useful when dealing with multiple test files.

By default, the preserveSpecsDir option is set to true, which replicates all directories preceding the test file under the coverage folder. However, for our purposes, having a single reports folder containing all reports is sufficient.

The coverage/cypress/sonarqube-report.xml file will look like this:

<?xml version="1.0" encoding="utf-8"?>
<testExecutions version="1">
  <file path="cypress/e2e/home.cy.ts">
    <testCase name="Navigation - should navigate to the about page" duration="685"></testCase>
    <testCase name="Navigation - should display - tested by cypress" duration="808"></testCase>
  </file>
</testExecutions>

And finally, we need to add the following configuration to the sonar-project.properties file:

sonar.testExecutionReportPaths=coverage/jest/sonarqube-report.xml,coverage/cypress/sonarqube-report.xml

This was a lot for a such small improvement, but now we can see the test cases count in the SonarQube report:

SonarQube report page showing test cases count

It is worth mentioning that the Unit tests count is not the same as the number of tests. Unit tests count is the number of assertions that were run.

For example, if you have a test containing two assertions, the Unit tests count will be 2. This number includes assertions from both sources - Jest and Cypress.

Example project

All the code from this blog post is available in the react-ssr-sonarqube-example opens a new window repository.

You can clone it and follow the steps from this blog post to see the results for yourself.

Conclusion

This blog post provided an in-depth guide on configuring Jest and Cypress to work with SonarQube for a JavaScript/TypeScript application.

We demonstrated how to set up the necessary configurations for generating coverage reports, displaying test coverage and test cases count in the SonarQube report.

Although some steps might seem complex, having a well-configured SonarQube instance can significantly improve the quality and maintainability of your codebase.

Upgrade.js can also help improve the quality and maintainability of your code base. Contact us opens a new window if you would like to learn more.