Setup Code Coverage for Monorepo — SonarQube

Vikas YadavVikas Yadav
2 min read

In a Monorepo setup, generating a coverage report for each library or app separately is common.

However, tools like the Sonar scanner may encounter difficulties in combining these reports into a single comprehensive one.

This issue arises when running tests with tools like nx affected, where only affected projects generate coverage reports, leaving unaffected projects out.

Consequently, SonarQube may raise quality gate errors due to missing coverage data.

Project Structure

Propose Solution Implemented

To resolve this problem, a custom script can be added to merge coverage reports into one file.

Below is a simple JavaScript function named coveragemerger.js for this purpose:

const glob = require('glob');
const fs = require('fs').promises;
const path = require('path');
const util = require('util');
const REPORTS_DIR_NAME = 'coverage';
const GREEN = '\x1b[32m%s\x1b[0m';
const BLUE = '\x1b[34m%s\x1b[0m';
const REPORTS_DIR_PATH = path.resolve(process.cwd(), REPORTS_DIR_NAME);
const globPromise = util.promisify(glob);
/**
 * Fetches all lcov.info files from coverage directories, excluding node_modules.
 * @returns {Promise<string[]>} A promise that resolves with an array of file paths.
 */
const getLcovFiles = function () {
 return globPromise(`**/coverage/lcov.info`, { ignore: '**/node_modules/**' });
};
/**
 * Creates a temp directory for all the reports.
 * @returns {Promise<void>} A promise that resolves when the directory has been created.
 */
async function createTempDir() {
 console.log(BLUE, `Creating a temp ${REPORTS_DIR_NAME} directory…`);
 try {
 await fs.mkdir(REPORTS_DIR_PATH, { recursive: true });
 console.log(GREEN, 'Done!');
 } catch (err) {
 console.error('Error creating directory:', err);
 }
}
(async function () {
 try {
 // Fetch all lcov.info files
 const files = await getLcovFiles();
 console.log("files are", files);
// Create temp directory
 await createTempDir();
// Read all files and join their contents
 const mergedReport = await Promise.all(files.map(file => fs.readFile(file, 'utf-8'))).then(contents => contents.join(''));
console.log(BLUE, `Copying the coverage report…`);
// Write the merged report to a new lcov.info file
 await fs.writeFile(path.resolve(REPORTS_DIR_PATH, `lcov.info`), mergedReport);
console.log('Code coverage has been saved!');
 } catch (err) {
 console.error('Error:', err);
 }
})();

To integrate this into your pipeline, add the following command to your Azure Pipeline file

- script: |
      node coverageMerger.js
   displayName: 'Merge Coverage Reports'

SonarQube Analysis Report

SonarQube Analysis — Monorepo

Reference:
use this site to check coverage from LCOV https://lcov-viewer.netlify.app/

0
Subscribe to my newsletter

Read articles from Vikas Yadav directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vikas Yadav
Vikas Yadav