How to Set up coverage analysis for both dotnet and angular in Azure DevOps

I’m trying to setup an AzureDevOps pipeline to report code coverage status as part of code quality metrics. The codebase includes both C# and Angular code.

I’m able to create separate coverage reports (in Cobertura format) and merge them under a common report which is visible under the Code Coverage tab in my Pipeline runs.

However, SonarQube is not able to find the Cobertura reports and keeps looking for .coverage files in the pre-defined location which results in error and 0% coverage reported in SonarQube side.

##[error]Failed to convert the binary code coverage reports to XML. No code coverage information will be uploaded to the server (SonarQube/SonarCloud).
##[error]Check that the downloaded code coverage file (C:\agent\_work\1\TestResults\***.cobertura.xml) is valid by opening it in Visual Studio. If it is not, check that the internet security settings on the build machine allow files to be downloaded from the Team Foundation Server machine.

Is there a way to make this work? Below is a snippet from my pipeline, it seems the extraProperties has no effect on SonarQubePrepare task:

# Prepare Analysis Configuration task
- task: SonarQubePrepare@7
  inputs:
    SonarQube: 'SonarQube'
    scannerMode: 'dotnet'
    msBuildVersion: '9.0.2.104486'
    projectKey: $(ProjectKey)
    extraProperties: |
      sonar.cs.vstest.reportsPaths=$(Common.TestResultsDirectory)/**/*.trx        
      sonar.cs.opencover.reportsPaths=$(Common.TestResultsDirectory)/CoverageResults/Cobertura.xml
      sonar.verbose=true

- task: Cache@2
  displayName: Cache SonarScanner
  inputs:
    key: '"SonarScanner" | ".NET" | "$(Agent.OS)"'
    path: '$(Agent.ToolsDirectory)/SonarScanner .NET'

- task: DotNetCoreCLI@2
  displayName: 'dotnet build'
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--no-restore --configuration $(buildConfiguration) 

- task: DotNetCoreCLI@2
  displayName: 'dotnet test'
  inputs:
    command: 'test'
    publishTestResults: false
    projects: 'tests/**/*.csproj'
    configuration: '$(buildConfiguration)'
    arguments: '--no-restore --no-build --configuration $(buildConfiguration) --logger trx  --results-directory "$(Common.TestResultsDirectory)" --collect "Code Coverage;Format=cobertura" --settings tests/coverlet.runsettings'

- script: |
    call npm install
    call npm run build --configuration=production
    
- task: Npm@1
  displayName: 'Test Angular'
  inputs:
    command: custom
    customCommand: run test-headless

- task: reportgenerator@5
  inputs:
    reports: '$(Build.SourcesDirectory)/**/coverage/**/*cobertura-coverage.xml;$(Common.TestResultsDirectory)/**/*.cobertura.xml'
    targetdir: '$(Common.TestResultsDirectory)/CoverageResults'
    reporttypes: 'HtmlInline_AzurePipelines;Cobertura;SonarQube'
    publishCodeCoverageResults: true

- task: PublishTestResults@2
  displayName: 'Publish dotnet test results'
  condition: succeededOrFailed()
  inputs:
      testResultsFormat: VSTest
      testResultsFiles: "$(Common.TestResultsDirectory)/**/*.trx" 

- task: PublishTestResults@2
  displayName: 'Publish Angular test results'
  condition: succeededOrFailed()
  inputs:
      testRunTitle: Angular
      testResultsFormat: JUnit
      testResultsFiles: "**/testresults/junit/unit-test-result.xml" 

# Run Code Analysis task
- task: SonarQubeAnalyze@7

# Publish Quality Gate Result task
- task: SonarQubePublish@7
  inputs:
    pollingTimeoutSec: '300'

Hey there.

.NET test Coverage can be reported to SonarQube in very specific, documented formats. Cobertura is not one of them.

Additionally, Javascript Code Coverage can only be reported via sonar.lcov.javascript.reportPaths (the exception would be usign SonarQube’s generic coverage data)

I suggest stepping back and reviewing both documentation pages so that you can generate the coverage reports in the right format to be read by SonarQube!

Hi Colin,

Thanks for your reply and pointing out that Cobertura is not used. I have confused it with other formats.

I need some clarifications:

  • It seems that SonarQube task always tries to take .coverage files even if they don’t exist. It will still try to find those files even if I supply a value to sonar.cs.opencover.reportsPaths. Is this the intended behavior? How do I disable taking visual studio test results if I want to use other format
  • Is there a way to change the base path for JavaScript code? SonarQube is able to read the coverage file from sonar.lcov.javascript.reportPaths but it complains about being unable to resolve the paths (test are executed from a sub directory and the coverage report lists the files relative to that directory).-

Hey @Mohamed_Ali

It would be helpful to see logs demonstrating both the behaviors you’re commenting on.

Hi @Colin,

Thanks for the reply. I have managed to fix the first issue and now able to pass coverage in opencover format. I had to use “XPlat Code Coverage;Format=cobertura,opencover” and pass the paramete to SonarQube accordingly.

I’m still facing issue with parsing lcov files. I 'm getting an error as below:

2025-01-28T09:09:27.7459694Z INFO: Analysing [C:\agent\_work\1\s\src\apps\Dashboard.WebApp\DashboardApp\coverage\DashboardApp\lcov.info]
2025-01-28T09:09:27.7688923Z WARN: Could not resolve 113 file paths in [C:\agent\_work\1\s\src\apps\Dashboard.WebApp\DashboardApp\coverage\DashboardApp\lcov.info]
2025-01-28T09:09:27.7690492Z WARN: First unresolved path: C:\agent\_work\1\s\src\apps\Dashboard.WebApp\DashboardApp\src\polyfills.ts (Run in DEBUG mode to get full list of unresolved paths)

I make sure the lcov file contains the full absolute path but it is not working. Does it require to use unix path format to be able to parse? The below line seems to indicate that it only splits by ‘/’.

My project is a mixture of C# and Angular, I couldn’t find a way to set a different path for the Angular code. Setting sonar.source will mess up the dotnet part.

Any idea?

HI @Colin,

Appreciate if you can update on this issue.

Hey @Mohamed_Ali

Thanks for the ping.

I think you’re onto something here – when I take a working coverage report and replace the forward slashes with backslashes, my coverage disappears.

You can test this yourself by swapping out backslashes for forward slashes in your coverage report.

I’ll flag this thread for attention.

Hi @Colin, I’m not able to use Windows absolute paths. The above example path C:\agent\_work\1\s\src\apps\Dashboard.WebApp\DashboardApp\src\polyfills.ts will show the warning for unresolved path. However, if I change the format to use / instead C:/agent/_work/1/s/src/apps/Dashboard.WebApp/DashboardApp/src/polyfills.ts, the warning disappears but the coverage is still not included correctly.

Would it be possible to know when this issue can be addressed?

Hi @Mohamed_Ali,

Thanks for reporting the issue. We have been trying to reproduce it, without success. It does not mean that it does not exist, though. There is likely something that we are missing with our testing setup. For context, we have been working for the last few weeks on improving (i.e. fixing) our LCOV support, and the more cases we can investigate, the better.

Would you mind preparing a minimal reproducer project, and share it with us through the platform of your choice?

Eric.