Previously Ive used Sonarqube to analyze without test coverage, recently Ive learnt how to use opencover to generate code coverage stats and use XunitXml.TestLogger to generate the test coverage report, this data is then used by Sonarqube to generate reports on the code base.
Software Setup
Sonarqube Server
- Start the container, the default login is
admin\admin
, here Im using v8.3 community
1 | docker run -d --name sonarqube83c -p 9000:9000 sonarqube:8.3-community |
Sonar Scanner
- Install the dotnet-sonarscanner tool into the path
c:\dev\sonardemo\tools
1 | dotnet tool install dotnet-sonarscanner --tool-path tools --version 5.0.4 |
.Net Core SDK
The source code Im going scan is netcoreapp2.2
, I think later versions will be backwards compatible so you could skip this step.
- Download and install SDK 2.2.207
On my machine this installs to C:\Program Files\dotnet\sdk\2.2.207
Setup Source Code
Clone And Build
- Clone the project VulnusCloud to
c:\dev\sonardemo\tmp
, I picked this project for a few reasons:
- it uses NUnit and I wanted to see if this would work the same as it does for a project using xUnit, turn out it does. FYI
xUnit
is more popular and its the default at most companys Ive worked for. - its got muiltiple test projects, I want to only run the UnitTests and ignore the IntegrationTest in the coverage report, currently the sln only references the UnitTests, I’ll include the IntegrationTest to test excluding them from the dotnet build and test steps.
Copy the contents of
C:\dev\sonardemo\tmp\VulnusCloud
toC:\dev\sonardemo\
so that the.sln
file is in the root, this just makes the sonar steps easier.Check the solution builds
1 | dotnet "C:\Program Files\dotnet\sdk\2.2.207\MSBuild.dll" . |
Add Dependencies
- We need to install XunitXml.TestLogger
1 | C:\dev\sonardemo\UnitTests |
- Additionally we need a collector, the de facto is coverlet.collector.
coverlet.collector
is a tool specifically designed to measure code coverage for .NET applications running on various platforms
1 | C:\dev\sonardemo\UnitTests |
- Create a
.runsettings
file inC:\dev\sonardemo\UnitTests
, thefriendlyName="XPlat code coverage"
refers to the measurement of how much of your codebase is executed during testing across different platforms (hence, “cross-platform” or “XPlat”)
1 |
|
Other examples from the docs include
1 | ... |
Runsettings can be added in UnitTests.csproj
with in the PropertyGroup, the downside is then when you run the tests from an IDE like Visual Studio it will create TestResults
folder in the root, adding to the csproj file is not required because we will pass it with the dotnet test
command so I just mention it here for completeness.
1 | <PropertyGroup> |
Also see
- https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/scanners/sonarscanner-for-dotnet/
- https://docs.sonarsource.com/sonarqube/latest/project-administration/analysis-scope/
Oh Its Scan time! 😬
You can use sonar.login
and pass a key like I did here but passwords belong in source code right? (I do what I want 🙈)
- Run scanner begin
1 | ./tools/dotnet-sonarscanner.exe begin ` |
- Run dotnet tests, if you dont specify the
--framework
argument it will use what ever version of dotnet thats in your systemsPATH
environmental variables, here Im rolling with .Net Core 2.2
1 | dotnet test VulnusCloud.sln ` |
Along with running the tests it creates the XML reports listed below, you could delete them as a run step, however sonarscanner keeps track of the based on the path but if this is running in a CI/CD pipeline on a volatile TeamCity agent the report file probably wont exist on the next run. Ta ta ma chance, uhambe kahle mfowethu ❤️
- C:\dev\sonardemo\UnitTests\TestResults\20cbc40e-1bb1-449e-a3e2-3d1a33c75315\coverage.opencover.xml
- C:\dev\sonardemo\UnitTests\TestResults\xunit.report.xml
- Run scanner end
1 | ./tools/dotnet-sonarscanner.exe end ` |
The code analysis can then be seen at http://localhost:9000/dashboard?id=VulnusCloud
xUnit Report Path Defaults
Adding sonar.cs.xunit.reportsPaths
is not actually required, if you omit it the report will be added as TestResults.xml
and picked up automagically, the resulting XML report paths are the same as the above. LogFilePath
is then also not needed for the dotnet test logger argument (is that an arguments, argument? LOL)
1 | ./tools/dotnet-sonarscanner.exe begin ` |
File Level Exclusions
- We need to install coverlet.msbuild for this to work
1 | C:\dev\sonardemo\UnitTests |
- Then add annotation
[ExcludeFromCodeCoverage] //Justification I do what I want
with valid justification. I’ve seen//NOSONAR
comments in some code bases but couldnt get it to work, maybe oneday I’ll figure it out and add it here.
ExcludeFromCodeCoverage examples
1 | namespace Business |
1 | ... |
We can then see the coverage value increases:
Additionally the file level exclusions no longer have the red line on the left:
Parameter Level Exclusions
See Analysis scope to understand more about pattern matching used below.
sonar.exclusions
Configure the files that should be completely ignored by the analysis, so this is things like bad code and code smells.
1 | -d:sonar.exclusions="**/Startup.cs,**/Program.cs,**/IoC/**,**/Dtos/**,**/Constants/**,**/Models/*" |
sonar.coverage.exclusions
Configure the files that should be ignored by code coverage calculations, so this is just for test coverage.
1 | -d:sonar.coverage.exclusions="**/SomeCrappyServiceThatIProbablyWontFixLater.cs,**/FooService/*" |