{"id":479413,"date":"2024-06-20T13:45:54","date_gmt":"2024-06-20T12:45:54","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=dotnet&#038;p=479413"},"modified":"2024-06-20T16:39:57","modified_gmt":"2024-06-20T15:39:57","slug":"dotcover-command-line-tools-for-automation-testing-code-coverage","status":"publish","type":"dotnet","link":"https:\/\/blog.jetbrains.com\/en\/dotnet\/2024\/06\/20\/dotcover-command-line-tools-for-automation-testing-code-coverage","title":{"rendered":"dotCover Command Line Tools for Automation Testing Code Coverage"},"content":{"rendered":"\n<p>The ultimate goal of creating a testing strategy is to be confident that you and your team have done their due diligence to deliver a quality product. Test harnesses typically include a mix of unit tests, integration testing, automation, and manual testing, each with advantages and disadvantages for your overall strategy. Determining the effectiveness of your plan requires data collected during test runs, and with code coverage reports, you quantify how each style of testing contributes to your confidence levels.<\/p>\n\n\n\n<p>While code coverage is strongly associated with unit testing libraries such as xUnit, NUnit, or MSTest, the JetBrains tool <a href=\"https:\/\/www.jetbrains.com\/dotcover\/\" target=\"_blank\" rel=\"noopener\">dotCover can provide code coverage<\/a> in many scenarios. In this post, we\u2019ll see how to install the dotCover command line tool and gather results from any .NET process, regardless of the execution environment, including local, CI\/CD, and production environments.<\/p>\n\n\n    <div class=\"buttons\">\n        <div class=\"buttons__row\">\n                                                <a href=\"https:\/\/www.jetbrains.com\/dotcover\/\" class=\"btn\" target=\"\" rel=\"noopener\">Download and try dotCover<\/a>\n                                                    <\/div>\n    <\/div>\n\n\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Getting Started with dotCover Command Line<\/h2>\n\n\n\n<p><a href=\"https:\/\/www.nuget.org\/packages\/JetBrains.dotCover.CommandLineTools\" target=\"_blank\" rel=\"noopener\"><strong>dotCover is available as a .NET CLI tool<\/strong><\/a><strong>, <\/strong>and you can install it in any environment where you can run a dotnet process. The command line tool supports Windows (x86\/x64\/ARM64), Linux (x64 \/ ARM \/ ARM64 \/ Musl x64 \/ Musl ARM64), and macOS (x64 \/ ARM64). You can install it either as a global or local tool.\u00a0<\/p>\n\n\n\n<p>From the command line, use the following command to install the tooling.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet tool install --global JetBrains.dotCover.CommandLineTools<\/pre>\n\n\n\n<p>Confirm the tool is installed globally by running the following command.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet-dotcover --help<\/pre>\n\n\n\n<p>Issues with global installations typically result from the installation folder for global tools not appearing in the PATH environment variable.<\/p>\n\n\n\n<p>For local installation to a .NET Solution, use the following command from the root of the solution folder.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet new tool-manifest\ndotnet tool install --local JetBrains.dotCover.CommandLineTools<\/pre>\n\n\n\n<p>Once installed locally, run the following command from the root of your solution to verify a correct installation.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet dotcover help<\/pre>\n\n\n\n<p>We\u2019re now ready to start code coverage testing any .NET application. Also, at any point, you can learn more about any command using the following format.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet dotcover help &lt;command><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Coverage Analysis for .NET Processes<\/h2>\n\n\n\n<p>I\u2019ve written a straightforward console application with various branching paths for this post. We\u2019ll see if our manual testing can verify that we\u2019ve hit all the branches correctly.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var run = true;\nConsole.WriteLine(\"Waiting for input...\");\n\nwhile (run)\n{\n    var key = Console.ReadKey();\n    \n    Console.Clear();\n\n    switch (key.Key)\n    {\n        case ConsoleKey.A:\n            Console.WriteLine(\"A is for ?\");\n            break;\n        case ConsoleKey.D:\n            Console.WriteLine(\"D is for ?\");\n            break;\n        case ConsoleKey.F:\n            Console.WriteLine(\"F is for ?\u200d?\");\n            break;\n        case ConsoleKey.S:\n            Console.WriteLine(\"S is for ?\");\n            break;\n        case ConsoleKey.X:\n            run = false;\n            Console.WriteLine(\"? Bye-Bye now\");\n            break;\n        default:\n            Console.WriteLine(\"? I have no clue\");\n            break;\n    }\n}\n<\/pre>\n\n\n\n<p>dotCover allows you to run any dotnet command under coverage analysis, with the most common entry points being exec and test. You can also start code coverage analysis on one or more currently running .NET processes.<\/p>\n\n\n\n<p>Since this application has no unit tests, we\u2019ll use exec to start our application and then manually attempt to hit each branch. <strong>dotCover requires that any process have PDBs available to provide coverage analysis correctly.<\/strong><\/p>\n\n\n\n<p>We\u2019ll use the following command from the command line to start a coverage analysis session on our console application. You&#8217;ll want to change the command to point to your project&#8217;s <code>dll<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet dotcover cover-dotnet --ReportType=HTML --TargetArguments=CoverageExample.dll --Output=.\/<\/pre>\n\n\n\n<p>This command launches our executable under coverage analysis, waiting for an exit code to stop the session.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">(base) ~\/RiderProjects\/CoverageExample\ndotnet dotcover cover-dotnet --ReportType=HTML --TargetArguments=CoverageExample.dll --Output=.\/\n? Bye-Bye now\n[JetBrains dotCover] Coverage session finished [5\/30\/2024 1:25:02PM]\n[JetBrains dotCover] Coverage results post-processing started [5\/30\/2024 1:25:02PM]\n[JetBrains dotCover] Report generation started [5\/30\/2024 1:25:02PM]\n[JetBrains dotCover] Report generation finished [5\/30\/2024 1:25:02PM]\n[JetBrains dotCover] Coverage results post-processing finished [5\/30\/2024 1:25:02PM]<\/pre>\n\n\n\n<p>Once completed, we can open our coverage report to see if we\u2019ve hit all the essential parts of our application logic.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"1309\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/05\/image-82.png\" alt=\"dotCover coverage analysis HTML report showing 80% coverage with covered and non-covered code.\" class=\"wp-image-479415\"\/><\/figure>\n\n\n\n<p>Very cool. Let\u2019s talk about some more scenarios.<\/p>\n\n\n                    <div class=\"alert \">\n            <p><strong>Note:<\/strong> You can <a href=\"https:\/\/www.jetbrains.com\/help\/dotcover\/Saving_and_Loading_Coverage_Snapshot.html\" target=\"_blank\" rel=\"noopener\">open dotCover reports directly in ReSharper<\/a> or dotCover and inspect differences between different runs.<\/p>\n        <\/div>\n    \n\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Helpful Tips and Tricks<\/h2>\n\n\n\n<p>While manual testing with coverage analysis is a fun example, you may need more practical testing strategies. The following sections will advise setting up more complex use cases with dotCover.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Download OS-specific tooling<\/h3>\n\n\n\n<p>While installing the dotCover tooling using the <code>dotnet<\/code> CLI is convenient for development environments. You can also <a href=\"https:\/\/www.jetbrains.com\/dotcover\/download\/other.html\" target=\"_blank\" rel=\"noopener\">download an OS-specific version from our download page<\/a>. The downloaded artifacts can help you build custom Docker images without needing an SDK-based base image or an internet connection to NuGet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Change a Container\u2019s Entry Point<\/h3>\n\n\n\n<p>Many folks rely on containerization to provide reliable and repeatable infrastructure for automation testing. As you\u2019ve seen previously, you can use the <code>dotnet-dotcover<\/code> command to launch your .NET applications. Change your Docker images to use the <code>dotnet-dotcover<\/code> process as your new <code>EntryPoint<\/code> and use the <code>--Output<\/code> flag to write the report to a persistent volume for future retrieval.<\/p>\n\n\n\n<p><strong>Rival Abdrakhmanov<\/strong> describes this process<strong> <\/strong>in his blog post, <a href=\"https:\/\/rafaelldi.github.io\/diagnostics-tools-inside-docker\/\" data-type=\"link\" data-id=\"https:\/\/rafaelldi.github.io\/diagnostics-tools-inside-docker\/\" target=\"_blank\" rel=\"noopener\">&#8220;Diagnostic tools inside Docker container&#8221;.<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Terminal Access to Containers<\/h3>\n\n\n\n<p>Sometimes, you can log on to running containers and execute the <code>dotnet-dotcover<\/code> command via orchestration. Terminal access can help you start and stop coverage analysis based on the lifecycle of automated tests such as Selenium or Playwright, which are a breeze to write with <a href=\"https:\/\/www.jetbrains.com\/aqua\/\" target=\"_blank\" rel=\"noopener\">JetBrains Aqua<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Merging Multiple Reports into One<\/h3>\n\n\n\n<p>Regardless of the number of unique processes under coverage analysis, you can always produce a single report. Use the <code>merge<\/code> command to integrate multiple sources into a single report.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet dotcover merge --source=Snapshot1.dcvr;Snapshot2.dcvr<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Think about adding an XML Configuration<\/h3>\n\n\n\n<p>You can manage dotCover settings in XML, making it easier to evolve and fine-tune the coverage analysis process. Create an XML file and pass it as an argument using the<code> --xm<\/code>l flag.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;ReportParams>\n    &lt;Source>&lt;!-- Coverage snapshot file name. -->&lt;\/Source>\n    &lt;Output>&lt;!-- Path to the resulting XML report. -->&lt;\/Output>\n    &lt;Output>&lt;!-- Path to the resulting JSON report. -->&lt;\/Output>\n    &lt;ReportType>XML&lt;\/ReportType>\n    &lt;ReportType>JSON&lt;\/ReportType>\n\n    &lt;!-- Remove auto-implemented properties from report.\n    &lt;HideAutoProperties>True&lt;\/HideAutoProperties>\n    -->\n\n    &lt;!-- Remove specified files from report. Ant-style patterns are supported here.\n    &lt;ExcludeFileMasks>\n      &lt;Mask>ProjectFolder\/**\/*.generated.cs&lt;\/Mask>\n      &lt;Mask>ProjectFolder\/**\/*.tmp.cs&lt;\/Mask>\n    &lt;\/ExcludeFileMasks>\n    -->\n  &lt;\/ReportParams>    <\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Don\u2019t Forget the help command<\/h3>\n\n\n\n<p>dotCover\u2019s built-in help menus offer great advice for every internal command. With the use of the <code>help<\/code> command, you can quickly figure out what to do, providing the support and confidence you need to use dotCover effectively.\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>As seen in this post, you can add dotCover outside typical unit testing scenarios. Adding automation testing using tools such as Selenium or Playwright doesn\u2019t mean you have to give up on code coverage, especially with the incredible power of dotCover.&nbsp;<\/p>\n\n\n\n<p>We hope you found this post helpful. Let us know if you have any questions.<\/p>\n\n\n\n<p><em><sub>image credit: <a href=\"https:\/\/unsplash.com\/@matthewhenry\" target=\"_blank\" rel=\"noopener\">Matthew Henry<\/a><\/sub><\/em><\/p>\n","protected":false},"author":1079,"featured_media":479429,"comment_status":"closed","ping_status":"closed","template":"","categories":[4992],"tags":[1130,211,441,214],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/479413"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/dotnet"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/1079"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=479413"}],"version-history":[{"count":9,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/479413\/revisions"}],"predecessor-version":[{"id":486226,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/479413\/revisions\/486226"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/479429"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=479413"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=479413"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=479413"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=479413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}