Ensuring “dotnet test” TRX & Coverage files end up in SonarQube
Do feel free to provide any comments/feedback to @TheRichCarey on Twitter
I have written before about using SonarQube to do static analysis, but one issue I never came back to was ensuring that code coverage files generated via a build pipeline end up being picked up by the Sonar Scanner to assess code coverage.
Note that the following I am actually using the ‘dotnet test’ build step, rather than the ‘Vs Test’ one. Do let me know if you find a nice work around for the VS Test variant, as I couldn’t get it to drop coverage files!
The issue
The issue is that:
- When using VSTest, TRX files are deleted automatically if using version 2+ of the VS Test task as per this stack overflow post.
- When I switched back to ‘dotnet test’ the same thing appeared to be happening.
- .coverage files are not output by default
- TRX and Coverage files are placed in a temporary folder of the build agent rather than the executing agents working directory.
- Even though SonarQube could detect the tests, it would still register as 0.0% code coverage!
Getting ‘dotnet test’ to collect coverage
The first step was to get the ‘dotnet test’ build step to collect the code coverage, and not just dump TRX files.
To do this, go to the “Arguments” field of the dotnet test build step and append --collect "Code Coverage"
, as well as ensuring that “Publish test results and code coverage” is enabled.
Ensure generated files are copied to the working directory
As the coverage files will end up in the /tmp folder of the build agent, SonarQube will not be able to scan them.
We will need to add a new build step of “copy files” with the correct filter set to get the .trx
and .coverage
files from the default temporary directory on the build agent, to the test results folder of the workspace. To do this we need to add the “Copy Files” task into the build and place it after the test task. The source folder for the copy will be $(Agent.HomeDirectory)\_work\_temp
and the target folder will be $(Common.TestResultsDirectory)
– The contents can remain as ** but feel free to filter if required. Example below.
If we run a build now, we should now see files in the TestResults folder of the build agent’s working directory.
I didn’t have to make any changes to the configuration within SonarQube as it should just pick up the coverage files. If I follow the above I get the following (Lets just ignore the fact the number is low 😉)
CSPROJ Changes to test projects
One thing I did notice in the console when attempting to fix this code coverage issue was that I got a lot of warnings like:
SonarQube.Integration.targets: warning : The project does not have a valid ProjectGuid. Analysis results for this project will not be uploaded to SonarQube.
As all my projects were .net core or .net standard the CSPROJ files do not contain a <ProjectGuid> tag by default. As also suggested in this stack overflow answer , I added a GUID to my test project file. I am not 100% if this is required, but it stopped warnings appearing in my console and does no harm.
Bonus
If you have multiple builds to update and you are using Azure Devops, you can take advantage of “Task Groups”. This allows you to create a single build step which in turn executes a series of other build steps. Using the steps above, you can create a new Task Group to create a single build step to run the test script and make sure the files are copied to the correct location for analysis. For example I have the single build step below:
Which means I can then just call this single build step in all my builds
In the first paragraph you are clearly mentioning that you don’t use ‘Vs Test’ task. In the other in the second task, you are saying we should add a Copy files task after the ‘Vs Test’.
Which one we should believe?
Hi. Apologies, this is a typo in the last section – corrected.
This guide originally used VS Test, but I encountered strange issues so switched it back to `dotnet test`
This 100% solved my problem, which has been driving me crazy for a couple of months. Thanks!
could you share what params you use on your SQ start command?
running in azure devops mine looks like this:
“C:\Program Files\dotnet\dotnet.exe” sonarscanner begin /k: /d:sonar.login=*** /d:sonar.host.url= /d:sonar.cs.xunit.reportsPaths=$(Build.StagingArtifactDirectory)/TestResults/*.trx /d:sonar.flex.cobertura.reportPaths=$(Build.StagingArtifactDirectory)/TestResults/Coverage/coverage.cobertura.xml
my project is dotnet core c#, i specified the sonar.flex.cobertura.reportPaths because we output the coverage to cobertura, not sure if that will work since my project is c#, but i can change that, i just need to know what works and i can adjust to that
TIA
ps: here’s my run test command:
dotnet test .csproj –logger trx –results-directory $(Build.StagingArtifactDirectory)\TestResults\ /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.StagingArtifactDirectory)\TestResults\Coverage\
nevermind my last post, I was able to get it all working through some trial and error, thanks
Glad you got it working! It sounded like you were using a manual command for starting the SQ run? I am currently using the official SQ devops plugin and the “Prepare analysis configuration” task which allows you to define additional properties in the editor.
I think both will have the same outcomes, but I do like the cleaner (and in theory auto-updating!) task.
Hi Jm, I am facing the same problem you did, can you tell me more how did get it published in sonar?
Thanks
Please use $(Agent.TempDirectory) variable instead of $(Agent.HomeDirectory)\_work\_temp to get the path of the temp directory
You are indeed correct! Whilst both will work, using $(Agent.TempDirectory) would be much cleaner. Thanks!