Search code examples
c#continuous-integrationgitlab-cigitlab-ci-runner

dotnet-reportgenerator is not able to find any coverage.cobertura.xml when running inside Gitlab CI pipeline


I want to generate a single coverage report for my C# project based on this tutorial

https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-code-coverage

When starting a Gitlab CI pipeline with the following configuration

image: mcr.microsoft.com/dotnet/sdk:5.0

stages:
  - build
  - unit-tests

build:
  stage: build
  script:
    - dotnet build --output build
  artifacts:
    paths:
      - build

unit-tests:
  stage: unit-tests
  script:
    - |-
      dotnet test --no-build --output build --collect:"XPlat Code Coverage";
      
      dotnet tool install -g dotnet-reportgenerator-globaltool;

      reportgenerator -reports:'**/coverage.cobertura.xml' -targetdir:'CoverageReports' -reporttypes:'Cobertura';
  artifacts:
    reports:
      cobertura: CoverageReports/Cobertura.xml
  dependencies:
    - build

I get the following error

The report file pattern '**/coverage.cobertura.xml' is invalid. No matching files found.

When using ls to inspect the directory before running the reportgenerator command I can see that there are no matching files (although they should exist).

I would expect to have one coverage.cobertura.xml file per test project e.g.

  • ...\myRepo\xUnitTestProject1\TestResults\380e65f7-48d5-468f-9cbc-550c8e0aeda8\coverage.cobertura.xml
  • ...\myRepo\xUnitTestProject2\TestResults\c96c55f7-40d3-483e-a136-573615e3a9c3\coverage.cobertura.xml

My current solution:

It seems I have to run the build again. So I replaced this line

dotnet test --no-build --output build --collect:"XPlat Code Coverage";

with

dotnet test --collect:"XPlat Code Coverage";

Now it's working fine but an additional build seems to be redundant because I already created a build artifact...


So do you have any ideas how to improve the configuration so that I don't have to build a second time?


Solution

  • It seems like you could combine the two steps since dotnet test will also trigger a build. You would have build problems reported from the same stage as tests, but both have similar responses.

    image: mcr.microsoft.com/dotnet/sdk:5.0
    
    stages:
      - unit-tests
    
    
    unit-tests:
      stage: unit-tests
      script:
        - |-
          dotnet test --output build --collect:"XPlat Code Coverage";
          
          dotnet tool install -g dotnet-reportgenerator-globaltool;
    
          reportgenerator -reports:'../coverage.cobertura.xml' -targetdir:'CoverageReports' -reporttypes:'Cobertura';
      artifacts:
        paths:
          - build
        reports:
          cobertura: CoverageReports/Cobertura.xml
    

    Alternatively: Coverlet Collector depends on CoreCompile. But, the Global tool does not (since it isn't an msbuild task).

    image: mcr.microsoft.com/dotnet/sdk:5.0
    
    stages:
      - build
      - unit-tests
    
    build:
      stage: build
      script:
        - dotnet build --output build
      artifacts:
        paths:
          - build
    
    unit-tests:
      stage: unit-tests
      script:
        - |-
          dotnet test --no-build --output build --collect:"XPlat Code Coverage";
    
          dotnet tool install -g coverlet.console;
    
          dotnet tool install -g dotnet-reportgenerator-globaltool;
    
          coverlet /path/to/test-assembly.dll --target "dotnet" --targetargs "test /path/to/test-project --no-build";
    
    
          reportgenerator -reports:'../coverage.cobertura.xml' -targetdir:'CoverageReports' -reporttypes:'Cobertura';
      artifacts:
        reports:
          cobertura: CoverageReports/Cobertura.xml
      dependencies:
        - build
    

    Coverlet's global tool also supports a --format cobertura option that may help to simplify this further.