{"id":51273,"date":"2024-04-03T10:05:00","date_gmt":"2024-04-03T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=51273"},"modified":"2024-04-02T12:47:34","modified_gmt":"2024-04-02T19:47:34","slug":"testing-your-native-aot-dotnet-apps","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/testing-your-native-aot-dotnet-apps\/","title":{"rendered":"Testing Your Native AOT Applications"},"content":{"rendered":"<p>We are happy to announce that we just published an early preview of support for testing Native AOT with MSTest, and we welcome all of you to try it.<\/p>\n<p>This new solution is powered by a completely new testing engine that we wrote from ground up, to allow running tests in Native AOT, and that works together with the existing MSTest runner (Microsoft.Testing.Platform). We also leveraged the power of source generators, to discover tests during compilation, which solved one of the big hurdles that prevented us from compiling and running tests in Native AOT.<\/p>\n<h2>What is Native AOT<\/h2>\n<p>.NET applications can be <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/deploying\/native-aot\">published as Native AOT<\/a>, which means that the resulting application is compiled ahead-of-time (AOT) into native code. Such applications have faster startup time, smaller memory footprint, and no dependency on .NET runtime.<\/p>\n<p>There are two main use cases for such applications. One is ASP.NET Core, which has added support for Native AOT in .NET 8, that takes advantage of the faster startup time. And the second use case is running applications directly on IoT devices that have limited resources and storage.<\/p>\n<h2>Why should I test Native AOT apps?<\/h2>\n<p>Native AOT applications can run in environments that do not allow JITing code. Such as game consoles, or some IoT devices. Building your tests as Native AOT will allow running them even on such devices.<\/p>\n<p>You may also be producing a Native AOT and non-native version of your application and use compiler conditions to take different paths in your code, e.g. to work around <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/deploying\/native-aot\/fixing-warnings#react-to-aot-warnings\">code that RequiresDynamicCode<\/a>.<\/p>\n<p>Native tests can help you test such use cases.<\/p>\n<h2>How to test your apps in Native AOT mode?<\/h2>\n<p>In the example below we are creating a test project that enables PublishAot and has 2 new packages installed: <code>MSTest.Engine<\/code> and <code>MSTest.SourceGeneration<\/code>. These packages contain our new testing engine, and source generator. We are using this test project to test our class library (not shown), that also enables PublishAot.<\/p>\n<pre><code class=\"language-xml\">&lt;!-- file: UnitTestProject1.csproj --&gt;\r\n&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;TargetFramework&gt;net8.0&lt;\/TargetFramework&gt;\r\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\r\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\r\n\r\n    &lt;OutputType&gt;exe&lt;\/OutputType&gt;\r\n    &lt;PublishAot&gt;true&lt;\/PublishAot&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n\r\n  &lt;ItemGroup&gt;\r\n    &lt;!-- \r\n      Experimental MSTest Engine &amp; source generator, \r\n      close sourced, licensed the same as our extensions \r\n      with Microsoft Testing Platform Tools license.\r\n    --&gt;\r\n    &lt;PackageReference Include=\"MSTest.Engine\" Version=\"1.0.0-alpha.24163.4\" \/&gt;\r\n    &lt;PackageReference Include=\"MSTest.SourceGeneration\" Version=\"1.0.0-alpha.24163.4\" \/&gt;\r\n\r\n    &lt;PackageReference Include=\"Microsoft.CodeCoverage.MSBuild\" Version=\"17.10.4\" \/&gt;\r\n    &lt;PackageReference Include=\"Microsoft.Testing.Extensions.CodeCoverage\" Version=\"17.10.4\" \/&gt;\r\n\r\n    &lt;PackageReference Include=\"Microsoft.Testing.Extensions.TrxReport\" Version=\"1.0.2\" \/&gt;\r\n    &lt;PackageReference Include=\"Microsoft.Testing.Platform.MSBuild\" Version=\"1.0.2\" \/&gt;\r\n    &lt;PackageReference Include=\"MSTest.TestFramework\" Version=\"3.2.2\" \/&gt;\r\n    &lt;PackageReference Include=\"MSTest.Analyzers\" Version=\"3.2.2\" \/&gt;\r\n\r\n  &lt;\/ItemGroup&gt;\r\n\r\n  &lt;ItemGroup&gt;\r\n    &lt;ProjectReference Include=\"..\\ClassLibrary1\\ClassLibrary1.csproj\" \/&gt;\r\n  &lt;\/ItemGroup&gt;\r\n\r\n  &lt;ItemGroup&gt;\r\n    &lt;Using Include=\"Microsoft.VisualStudio.TestTools.UnitTesting\" \/&gt;\r\n  &lt;\/ItemGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/code><\/pre>\n<pre><code class=\"language-csharp\">\/\/ file: UnitTest1.cs\r\nusing ClassLibrary1;\r\n\r\nnamespace TestProject1;\r\n\r\n[TestClass]\r\npublic class UnitTest1\r\n{\r\n    [TestMethod]\r\n    public void TestMethod1()\r\n    {\r\n        Assert.AreEqual(3, new Class1().Add(1, 2));\r\n    }\r\n\r\n    [TestMethod]\r\n    [DataRow(1, 2)]\r\n    public void TestMethod2(int left, int right)\r\n    {\r\n        Assert.AreEqual(3, new Class1().Add(left, right));\r\n    }\r\n}<\/code><\/pre>\n<p>The complete example is available at <a href=\"https:\/\/github.com\/microsoft\/testfx\/tree\/main\/samples\/mstest-runner\/NativeAotRunner\">microsoft\/testfx samples<\/a>.<\/p>\n<p>To run your tests, you need to publish them, then run the published executable:<\/p>\n<pre><code class=\"language-bash\">C:\\t\\NativeAotRunner&gt; dotnet publish .\\TestProject1 --runtime win-x64\r\nC:\\t\\NativeAotRunner&gt; .\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestProject1.exe\r\n\r\nMicrosoft(R) Testing Platform Execution Command Line Tool\r\nCopyright(c) Microsoft Corporation.  All rights reserved.\r\n\r\nPassed! - Failed: 0, Passed: 6, Skipped: 0, Total: 6, Duration: 7ms - TestProject1.exe<\/code><\/pre>\n<h2>Code Coverage and TRX reports<\/h2>\n<p>You might notice that the example project is referencing the CodeCoverage and TRX report extension packages. They are, as their name suggests, providing Code Coverage and TRX (test results) reports.<\/p>\n<p>We think this is the minimum that you need to successfully add Native AOT tests to your existing pipelines, and that is why we are providing them with this early preview. Using the project above you can simply provide <code>--coverage --report-trx<\/code> on the commandline to collect TRX and CodeCoverage report.<\/p>\n<pre><code class=\"language-bash\">C:\\t\\NativeAotRunner&gt; .\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestProject1.exe --coverage --report-trx\r\n\r\nMicrosoft(R) Testing Platform Execution Command Line Tool\r\nCopyright(c) Microsoft Corporation.\u00a0 All rights reserved.\r\n\r\nPassed! - Failed: 0, Passed: 6, Skipped: 0, Total: 6, Duration: 1ms - TestProject1.exe\r\n\r\nIn process file artifacts produced:\r\n- C:\\t\\NativeAotRunner\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestResults\\jajares_RDESKTOP_2024-03-27_12_18_20.727.trx\r\n- C:\\t\\NativeAotRunner\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestResults\\8abd5db9-4d12-4778-b4f1-e24635c44df0.coverage<\/code><\/pre>\n<p>Here are the details of how this is set up:<\/p>\n<h3>Running with TRX<\/h3>\n<p>To run with TRX report provide <code>--report-trx<\/code> on commandline.<\/p>\n<p><code>.\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestProject1.exe --report-trx<\/code><\/p>\n<h3>Running with Code Coverage<\/h3>\n<p>To generate code coverage in Native AOT mode you need to instrument the executable during <code>dotnet publish<\/code>. This is done by providing <code>\/p:AotMsCodeCoverageInstrumentation=true<\/code> MSBuild property:<\/p>\n<p><code>dotnet publish .\\TestProject1 --runtime win-x64 \/p:AotMsCodeCoverageInstrumentation=true<\/code><\/p>\n<p>And then during test run you will provide <code>--coverage<\/code> parameter to collect code coverage of the run:<\/p>\n<p><code>.\\TestProject1\\bin\\Release\\net8.0\\win-x64\\publish\\TestProject1.exe --coverage<\/code><\/p>\n<p>This functionality is achieved by the two referenced NuGet packages:<\/p>\n<ul>\n<li><code>Microsoft.CodeCoverage.MSBuild<\/code>: adds required MSBuild targets to instrument test project during build<\/li>\n<li><code>Microsoft.Testing.Extensions.CodeCoverage<\/code>: adds coverage provider to test<\/li>\n<\/ul>\n<p>For more information please visit <a href=\"https:\/\/github.com\/microsoft\/codecoverage\/blob\/main\/samples\/Algorithms\/scenarios\/scenario06\/README.md\">Code coverage for MSTest runner project in Native AOT mode<\/a>.<\/p>\n<p>To see how you can collect coverage for any Native AOT console app, using <code>Microsoft.CodeCoverage.MSBuild<\/code> and <code>dotnet-coverage<\/code> tool please see <a href=\"https:\/\/github.com\/microsoft\/codecoverage\/blob\/main\/samples\/Algorithms\/scenarios\/scenario05\/README.md\">this example<\/a>.<\/p>\n<h2>Limitations<\/h2>\n<p><code>MSTest.Engine<\/code> and <code>MSTest.SourceGeneration<\/code> are implementing the bare minimum to run tests:<\/p>\n<ul>\n<li>They detect <code>[TestClass]<\/code> and <code>[TestMethod]<\/code> attributes.<\/li>\n<li>Test methods can use <code>[DataRow]<\/code> and <code>[DynamicData]<\/code>.<\/li>\n<\/ul>\n<p>All other features of MSTest are <strong>not supported<\/strong> yet. This includes:<\/p>\n<ul>\n<li>Inheriting from <code>TestClass<\/code> and <code>TestMethod<\/code> attributes is not supported.<\/li>\n<li><code>AssemblyInitialize<\/code>, <code>ClassInitialize<\/code>, <code>TestInitialize<\/code>, as well as all the related Cleanups are not supported.<\/li>\n<li>There is no way to configure in-process parallelism, all tests will run in parallel, as if you defined <code>[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]<\/code>.<\/li>\n<\/ul>\n<h2>What\u2019s next?<\/h2>\n<p>We are adding the various <code>Initialize<\/code> and <code>Cleanup<\/code> methods, which was a top request from the feature\u2019s early adopters.<\/p>\n<p>We are also working on improving the getting started experience via a new <code>MSTest.SDK<\/code>, we will announce this soon!<\/p>\n<p>For more features that you would like to prioritize, please let us know in this issue GitHub issue, where we are collecting feedback <a href=\"https:\/\/github.com\/microsoft\/testfx\/issues\/1837\">Add support for NativeAOT<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MSTest introduces a Native AOT compatible test runner and engine for testing your Native AOT applications.<\/p>\n","protected":false},"author":138175,"featured_media":51274,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756,3009],"tags":[58,63,7784,136],"class_list":["post-51273","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","category-performance","tag-csharp","tag-dotnet","tag-mstest","tag-testing"],"acf":[],"blog_post_summary":"<p>MSTest introduces a Native AOT compatible test runner and engine for testing your Native AOT applications.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/51273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/138175"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=51273"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/51273\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/51274"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=51273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=51273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=51273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}