You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SonarQube与Azure DevOps代码覆盖率不一致问题求助

问题:Azure Pipelines中SonarQube覆盖率远低于Azure DevOps,特定项目未统计覆盖率

在Azure Pipelines中对.NET Core项目执行Sonar分析时,出现以下问题:

  • Azure DevOps的代码覆盖率报告显示正常,但SonarQube上的覆盖率数值低很多
  • 排查发现,测试项目引用的Setup.csproj和Services.csproj对应的代码文件夹,在SonarQube中能看到代码,但没有生成覆盖率数据
  • 使用Standalone scanner时SonarQube覆盖率正常,但切换为MSBuild扫描器时问题复现,且未设置任何显式的包含/排除规则

现有Pipeline配置

trigger:
- sonar-analysis

pool:
  vmImage: 'ubuntu-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:

- checkout: self
  persistCredentials: true
  submodules: true

- task: SonarQubePrepare@5
  inputs:
    SonarQube: 'sonar-connection'
    scannerMode: 'MSBuild'
    projectKey: 'proj'
    projectName: 'proj'
    extraProperties: |
      sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml
      sonar.verbose=true
      sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/**/*.trx

- task: UseDotNet@2
  inputs:
    version: '6.x'

- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: '**/**/*.csproj'
    arguments: '--verbosity n'

- task: DotNetCoreCLI@2
  displayName: 'dotnet test'
  inputs:
    command: 'test'
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura,opencover'
  
- task: PublishCodeCoverageResults@1
  displayName: 'Publish code coverage report'
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'

- task: SonarQubeAnalyze@5
  inputs:
    jdkversion: 'JAVA_HOME_11_X64'

- task: SonarQubePublish@5
  inputs:
    pollingTimeoutSec: '300'

测试项目Test.csproj内容

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>


  <ItemGroup>
    <PackageReference Include="AutoMapper" Version="12.0.1" />
    <PackageReference Include="coverlet.msbuild" Version="6.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
    <PackageReference Include="Moq" Version="4.18.2" />
    <PackageReference Include="xunit" Version="2.4.2" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="coverlet.collector" Version="3.2.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Core\Setup\Setup.csproj" />
    <ProjectReference Include="..\..\Infrastructure\Services\Services.csproj" />
  </ItemGroup>

</Project> 

原因分析
  1. MSBuild扫描器的项目绑定机制:MSBuild扫描器需要在构建阶段将SonarQube分析逻辑注入到每个项目的构建过程中。如果Setup.csproj和Services.csproj在SonarQubePrepare任务之后的构建中没有被正确处理(比如路径匹配遗漏),SonarQube会将这些项目的代码视为"未被分析的代码",从而忽略对应的覆盖率数据。
  2. 覆盖率报告路径匹配问题:coverlet生成的opencover报告中,文件路径可能和SonarQube扫描到的文件路径不一致(比如相对路径/绝对路径差异、大小写差异),导致SonarQube无法匹配到对应代码的覆盖率数据。
  3. 测试任务的覆盖率收集范围:虽然测试项目引用了这两个项目,但coverlet可能没有正确收集到这两个项目的覆盖率数据,或者报告中未包含这些项目的信息。

解决步骤

1. 修正构建任务,确保所有目标项目被SonarQube注入分析逻辑

将DotNetCoreCLI@2的build任务改为使用solution变量,避免路径匹配遗漏:

- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: '$(solution)'
    arguments: '--configuration $(buildConfiguration) --verbosity n'

如果必须指定csproj,确保路径匹配包含目标项目:

projects: '**/*.csproj'

(原配置中的**/**/*.csproj是冗余的,可能导致部分项目被遗漏)

2. 确保覆盖率报告路径完全匹配

在SonarQubePrepare的extraProperties中添加路径匹配配置,同时显式指定测试任务的覆盖率输出路径:

# SonarQubePrepare任务的extraProperties新增配置
extraProperties: |
  sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml
  sonar.verbose=true
  sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/**/*.trx
  sonar.cs.coveragePaths=$(Build.SourcesDirectory)/**/*.cs
# 测试任务调整输出路径
- task: DotNetCoreCLI@2
  displayName: 'dotnet test'
  inputs:
    command: 'test'
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura,opencover DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Output=$(Agent.TempDirectory)/coverage'

3. 显式指定SonarQube的包含规则

在SonarQubePrepare的extraProperties中添加包含规则,确保目标项目代码被纳入分析:

extraProperties: |
  sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml
  sonar.verbose=true
  sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/**/*.trx
  sonar.inclusions=**/Core/Setup/**/*.cs,**/Infrastructure/Services/**/*.cs

4. 强制指定测试任务的覆盖率收集范围

在测试命令中显式指定要收集覆盖率的目标项目,避免coverlet遗漏:

arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura,opencover DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="[*]Core.Setup*,[*]Infrastructure.Services*"'

5. 验证SonarQube的项目分析状态

利用sonar.verbose=true开启详细日志,查看SonarQubeAnalyze任务的日志输出,确认Setup.csproj和Services.csproj是否被纳入分析。如果日志中没有相关记录,说明构建阶段未正确注入SonarQube逻辑,需要重新调整构建任务的项目范围。


内容的提问来源于stack exchange,提问作者Ajinkya Bapat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:24:56