[HOWTO] Report code coverage of .NET solution in SonarCloud when using Azure DevOps Microsoft-hosted linux agent

As mentioned in the linked blog post and in the official SonarCloud docs, code coverage reporting works out of the box if and only if your .NET build is running on Windows agents. If you are running your .NET build on Linux agents, some adjustments are required.

To make SonarCloud code coverage reporting work on Azure DevOps Microsoft-hosted linux agents, I will make use of .NET tool scanner variant dotnet-coverage (for more details, see here).

I will reuse the YAML pipeline files of the blog post linked above and highlight the necessary adjustments in the code snippets.

ArbitrarySolution-quality.yml

Due to the usage of dotnet-coverage, it’s no longer possible to use Azure DevOps pipeline task UseDotNet@2 for executing the tests. And because dotnet-coverage does not support multiple projects nor wildcards, I pass the solution folder instead.

name: ArbitrarySolution Quality Pipeline
# pipeline gets triggered on every commit to dev or main branch, if there are changes in src and/or deploy directory
trigger:
  branches:
    include:
      - dev
      - main
  paths:
    include:
      - src
      - deploy
# as we are using yarn (v3.6.3), we specify the folder for the yarn cache
variables:
  YARN_CACHE_FOLDER: "$(Pipeline.Workspace)/.yarn"
  CI: true
stages:
  - stage: Quality
    displayName: Quality Build
    pool:
      vmImage: ubuntu-latest
    jobs:
      - template: jobs-build-quality.yml
        parameters:
          dotNetBuildProjects: "**/*.sln"
          dotNetSolutionFolder: "/home/vsts/work/1/s/"

jobs-build-quality.yml

The solution folder is the referred as base path for sonar.cs.vscoveragexml.reportsPaths and as directory in the dotnet test command. Note that the dotnet test command is now passed as an argument to the dotnet-coverage collect command.

parameters:
  - name: dotNetBuildProjects
    type: string
    default: "**/*.sln"
  - name: dotNetSolutionFolder
    type: string
  - name: buildConfiguration
    type: string
    default: "Release"
  - name: "clientPath"
    type: string
    default: "src/Client"
 
jobs:
  - job: Build
    displayName: Quality Build ArbitrarySolution
    pool:
      vmImage: ubuntu-latest
    steps:
      - checkout: self
        fetchDepth: 0 # disable shallow fetch to avoid SCM warnings in SonarCloud (see https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps-checkout?view=azure-pipelines#shallow-fetch)
      - task: UseDotNet@2
        displayName: Get DotNet SDK
        inputs:
          packageType: sdk
          useGlobalJson: true # .NET version is specified in global.json file which resides in the repository root
      - task: SonarCloudPrepare@2
        displayName: "Prepare analysis configuration"
        inputs:
          SonarCloud: "AZ_DEVOPS_SERVICE_CONNECTION_NAME_HERE"
          organization: NAME_OF_THE_SONARCLOUD_ORGANIZATION_HERE
          projectKey: SONARCLOUD_PROJECT_KEY_HERE # if project already exists in SonarCloud, get the project key from the project settings
          extraProperties: |
            scm.provider=git
            sonar.cs.vscoveragexml.reportsPaths=${{parameters.dotNetSolutionFolder}}/coverage.xml
      - task: DotNetCoreCLI@2
        displayName: Build ArbitrarySolution Backend
        inputs:
          command: build
          projects: ${{parameters.dotNetBuildProjects}}
          arguments: "--configuration ${{parameters.buildConfiguration}}"
      - script: dotnet tool install --global dotnet-coverage
        displayName: Install dotnet-coverage
      - script: dotnet-coverage collect "dotnet test ${{parameters.dotNetSolutionFolder}} --configuration ${{parameters.buildConfiguration}}" -f xml -o "coverage.xml"
        displayName: Test ArbitrarySolution Backend
      - task: NodeTool@0
        inputs:
          versionSpec: "18.17.x"
      - task: Bash@3
        displayName: Yarn - Install
        inputs:
          targetType: inline
          workingDirectory: ${{parameters.clientPath}}
          script: |
            yarn set version 3.6.3
      - task: Bash@3
        displayName: Yarn - Install NPM packages
        inputs:
          targetType: inline
          workingDirectory: ${{parameters.clientPath}}
          script: |
            yarn install --immutable
      - task: Bash@3
        displayName: Yarn - Run jest
        inputs:
          targetType: inline
          workingDirectory: ${{parameters.clientPath}}
          script: |
            yarn test-ci
      - task: SonarCloudAnalyze@2
        displayName: SonarCloud - Run code analysis
      - task: SonarCloudPublish@2
        displayName: SonarCloud - Publish quality gate result

That’s it. Please note that only the changed or added lines of code are taken into account for the calculation of code coverage in pull request analyses!

Useful links

Leave a Reply

Powered by WordPress.com.

Up ↑

Discover more from blog.rufer.be

Subscribe now to keep reading and get access to the full archive.

Continue reading