{"id":55864,"date":"2025-03-19T10:05:00","date_gmt":"2025-03-19T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=55864"},"modified":"2025-03-19T10:05:00","modified_gmt":"2025-03-19T17:05:00","slug":"mstest-3-8-highlights","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/mstest-3-8-highlights\/","title":{"rendered":"MSTest 3.8: Top 10 features to supercharge your .NET tests!"},"content":{"rendered":"<p>MSTest, also known as Microsoft Testing Framework, simplifies the testing experience for .NET applications. This test framework allows users to write and execute tests while providing test suites integrated into the Test Explorer&#8217;s of Visual Studio and Visual Studio Code, as well as many CI pipelines. Fully supported, open-source, and cross-platform, MSTest works with all supported .NET targets while maintaining support for VSTest as well as adding support for the improved experience with <a href=\"https:\/\/aka.ms\/mtp-overview\">Microsoft.Testing.Platform (MTP)<\/a>.<\/p>\n<h2>MSTest 3.8 highlights<\/h2>\n<p>We continually work to improve MSTest, address your feedback and offer smoother testing experience. MSTest 3.8 is a big release, with lots of great new features:<\/p>\n<ul>\n<li>\u2696\ufe0f <strong>Filtering<\/strong>: The VSTestBridge extension of Microsoft.Testing.Platform (MTP) 1.6 and later now supports filtering while discovering tests (&#8211;list-tests). This feature has been highly requested by users to help verify the tests applicable to a given filter.<\/li>\n<li>\u25b6\ufe0f <strong>Running tests with MSBuild<\/strong>: Extended support for the -t:Test or \/t:Test target to support MSBuild.exe.<\/li>\n<li>\ud83d\udd04 <strong>Improved iterating experience<\/strong>: Updated the Microsoft.Testing.Extensions.TrxReport extension behavior to overwrite the content instead of failing when the TRX file already exists.<\/li>\n<li>\ud83d\udce6 <strong>Enriched metapackage<\/strong>: Simplified using TRX report and Code Coverage by removing the requirement to manually declare these MTP extensions.<\/li>\n<li>\ud83c\udf10 <strong>Support of modern UWP<\/strong>: MSTest now supports .NET 9 in UWP.<\/li>\n<li>\u2705 <strong>Improved assertions<\/strong>: Introduced new assertion APIs like Assert.Throws, Assert.ThrowsAsync, Assert.ThrowsExactly, and Assert.ThrowsExactlyAsync.<\/li>\n<li>\ud83d\udcca <strong>Better data-driven tests<\/strong>: Parameterized generic test methods are now supported.<\/li>\n<li>\ud83d\udd04 <strong>Retrying flaky tests<\/strong>: Introduced RetryAttribute to automatically retry failing test methods.<\/li>\n<li>\ud83e\udd14 <strong>Introducing conditional tests<\/strong>: Added OSConditionAttribute for fine-grained control over where your tests run.<\/li>\n<li>\ud83d\udd0d <strong>Analyzers<\/strong>: The MSTestAnalysisMode feature allows you to control the severity levels of code analyzers within your test projects.<\/li>\n<\/ul>\n<p>Read on for more, and check out <a href=\"https:\/\/github.com\/microsoft\/testfx\/compare\/v3.7.3...v3.8.0\">the full changelog on GitHub<\/a> for details.<\/p>\n<h3>Filtering<\/h3>\n<p>The VSTestBridge extension of Microsoft.Testing.Platform (MTP) 1.6 and later now supports filtering while discovering tests (<code>--list-tests<\/code>). This feature has been highly requested by users to help verify the tests applicable to a given filter. This gives more confidence that you have the correct filter when you run tests.<\/p>\n<p>Because MSTest with MTP uses the VSTestBridge, that feature is available in MSTest 3.8 and later.<\/p>\n<p>Based on your requests, we also added support for the runsettings <code>TestCaseFilter<\/code> element in MTP. Note that this is also available for any test framework relying on <code>Microsoft.Testing.Extensions.VSTestBridge<\/code> extension.<\/p>\n<h3>Running tests with MSBuild<\/h3>\n<p>Another of your requests was to extend the support of the <code>-t:Test<\/code> or <code>\/t:Test<\/code> target that was a <code>dotnet build<\/code> only feature to support MSBuild.exe. This is now available for MSTest 3.8, and any test framework based on MTP 1.6+.<\/p>\n<h3>Improved iterating experience<\/h3>\n<p>We updated the <code>Microsoft.Testing.Extensions.TrxReport<\/code> extension behavior when the TRX file already exists to overwrite the content instead of failing. The extension will display a warning message to notify that the file already exists and will be overwritten.<\/p>\n<p>For example, when running the following command twice:<\/p>\n<pre><code class=\"language-console\">dotnet run -- --report-trx --report-trx-filename test.trx<\/code><\/pre>\n<p>The second run will not fail, but instead show the following warning:<\/p>\n<pre><code class=\"language-console\">Warning: Trx file 'path\/to\/TestResults\/test.trx' already exists and will be overwritten.<\/code><\/pre>\n<h3>Enriched metapackage<\/h3>\n<p>Since most MSTest projects use both TRX report and Code Coverage, we&#8217;ve simplified using them by removing the requirement to manually declare these MTP extensions. With MSTest 3.8 and MTP, you can simplify your projects from:<\/p>\n<pre><code class=\"language-xml\">&lt;ItemGroup&gt;\n  &lt;PackageReference Include=\"MSTest\" \/&gt;\n  &lt;PackageReference Include=\"Microsoft.Testing.Extensions.CodeCoverage\" \/&gt;\n  &lt;PackageReference Include=\"Microsoft.Testing.Extensions.TrxReport\" \/&gt;\n&lt;\/ItemGroup&gt;<\/code><\/pre>\n<p>to:<\/p>\n<pre><code class=\"language-xml\">&lt;ItemGroup&gt;\n  &lt;PackageReference Include=\"MSTest\" \/&gt;\n&lt;\/ItemGroup&gt;<\/code><\/pre>\n<h3>Support of modern UWP<\/h3>\n<p>UWP recently announced support for .NET 9 in <a href=\"https:\/\/devblogs.microsoft.com\/ifdef-windows\/preview-uwp-support-for-dotnet-9-native-aot\/\">Modernize your UWP app with preview UWP support for .NET 9 and Native AOT blog post<\/a> and through close collaboration, we are happy to announce that MSTest supports this new mode. For that to work, you need to be using <code>Microsoft.NET.Test.Sdk<\/code> version <code>17.14.0-preview-25107-01<\/code> or above. Stay tuned for a dedicated blog post with all the information and examples coming soon.<\/p>\n<h3>Improved assertions<\/h3>\n<h4>Exception asserts<\/h4>\n<p>Historically, only <code>Assert.ThrowsException<\/code> and <code>Assert.ThrowsExceptionAsync<\/code> were available, requiring an exact match between the specified exception type and the one thrown. Many users requested a simpler naming convention and support for derived exceptions. In response, this release introduces:<\/p>\n<ul>\n<li><code>Assert.Throws<\/code><\/li>\n<li><code>Assert.ThrowsAsync<\/code><\/li>\n<li><code>Assert.ThrowsExactly<\/code><\/li>\n<li><code>Assert.ThrowsExactlyAsync<\/code><\/li>\n<\/ul>\n<p>To facilitate a smooth upgrade to MSTest 3.8 without causing disruptions to your projects, the old APIs remain available. However, when creating new tests, they will not appear in IntelliSense, encouraging the use of simpler names.<\/p>\n<p>The following is an example of usage of the new APIs:<\/p>\n<pre><code class=\"language-csharp\">[TestClass]\npublic sealed class Test1\n{\n    [TestMethod]\n    public void TestMethod1()\n    {\n        \/\/ Will pass because the exceptions have the exact same type\n        Assert.Throws&lt;OperationCanceledException&gt;(() =&gt; throw new OperationCanceledException());\n\n        \/\/ Will pass because TaskCanceledException derives from OperationCanceledException\n        Assert.Throws&lt;OperationCanceledException&gt;(() =&gt; throw new TaskCanceledException());  \n\n        \/\/ Will fail because the exceptions do not have exactly the same type\n        Assert.ThrowsExactly&lt;OperationCanceledException&gt;(() =&gt; throw new TaskCanceledException());\n    }\n}<\/code><\/pre>\n<p>We also include MSTEST0039 analyzer along with a codefix to help you with transitioning away from the old APIs to the new ones.<\/p>\n<p>In addition, we added new overloads to the new APIs that accepts a <code>messageBuilder<\/code> parameter, which is a <code>Func&lt;Exception?, string&gt;<\/code>. This allows you to customize the failure message based on information from the exception thrown (if any). For example:<\/p>\n<pre><code class=\"language-csharp\">[TestMethod]\npublic void TestMethod1()\n{\n    Assert.ThrowsExactly&lt;InvalidOperationException&gt;(\n        () =&gt; throw new ArgumentException(\"Message of ArgumentException\"),\n        messageBuilder: actualException =&gt;\n        {\n            if (actualException is null)\n            {\n                return \"Expected InvalidOperationException but no exception was thrown.\";\n            }\n\n            return $\"Expected InvalidOperationException, but found {actualException.GetType().Name} with message {actualException.Message}\";\n        });\n}<\/code><\/pre>\n<p>The above test will fail as follows:<\/p>\n<pre><code class=\"language-console\">  Assert.ThrowsExactly failed. Expected exception type:&lt;System.InvalidOperationException&gt;. Actual exception type:&lt;System.ArgumentException&gt;. Expected InvalidOperationException, but found ArgumentException with message Message of ArgumentException<\/code><\/pre>\n<h4>Collection asserts<\/h4>\n<p>We introduced new collection-related assertion APIs in the <code>Assert<\/code> class, designed to work with <code>IEnumerable&lt;T&gt;<\/code>, expanding the assertion capabilities for collections:<\/p>\n<ul>\n<li><code>Assert.HasCount<\/code>: Asserts that an <code>IEnumerable&lt;T&gt;<\/code> has a given number of elements.<\/li>\n<li><code>Assert.IsEmpty<\/code>: Asserts that an <code>IEnumerable&lt;T&gt;<\/code> doesn&#8217;t have any elements.<\/li>\n<li><code>Assert.IsNotEmpty<\/code>: Asserts that an <code>IEnumerable&lt;T&gt;<\/code> has at least one element.<\/li>\n<li><code>Assert.ContainsSingle<\/code>: Asserts that an <code>IEnumerable&lt;T&gt;<\/code> has exactly a single element and returns that element.<\/li>\n<\/ul>\n<p>These new APIs have been added to the <code>Assert<\/code> class instead of <code>CollectionAssert<\/code> to improve discoverability. We also plan to make all <code>CollectionAssert<\/code> APIs available on <code>Assert<\/code>.<\/p>\n<h4>Interpolated string handler overloads for message parameter<\/h4>\n<p>Finally, we added new interpolated string handler overloads for many existing assertion APIs. If you heavily use the <code>Assert<\/code> methods overload with <code>message<\/code> parameter and use an interpolated string as argument to the message parameter, this will unnecessarily allocate a string that will end up being unused in most cases, as this message is only used in case the assertion failed. Starting with 3.8, new overloads that use interpolated string handler will ensure that the string isn&#8217;t allocated unless the assertion fails.<\/p>\n<h3>Better data-driven tests<\/h3>\n<p>Parameterized generic test methods are now supported, allowing you to write tests like:<\/p>\n<pre><code class=\"language-csharp\">[TestClass]\npublic sealed class TestClass1 \n{\n    [TestMethod]\n    [DataRow(2, 5, 10)]\n    [DataRow(2.0d, 5.0d, 10.0d)]\n    [DataRow(5, 5, 25)]\n    [DataRow(5.0d, 5.0d, 25.0d)]\n    public void TestMethod1&lt;T&gt;(T number1, T number2, T expectedResult) where T : IMultiplyOperators&lt;T, T, T&gt;\n    {\n        Assert.AreEqual(expectedResult, number1 * number2);\n    }\n}<\/code><\/pre>\n<p>Additionally, we&#8217;ve enhanced the behavior of <code>DynamicDataAttribute<\/code> to automatically detect the type of the referenced member used as the data source as the default. With this improvement, you can simplify your tests from:<\/p>\n<pre><code class=\"language-csharp\">[DynamicData(nameof(MyProperty), DynamicDataSourceType.Property)]\n[DynamicData(nameof(MyMethod), DynamicDataSourceType.Method)]<\/code><\/pre>\n<p>to:<\/p>\n<pre><code class=\"language-csharp\">[DynamicData(nameof(MyProperty))]\n[DynamicData(nameof(MyMethod))]<\/code><\/pre>\n<p>Another highly requested feature was the ability to ignore specific data sources from parameterized tests. Starting in 3.8, <code>DataRowAttribute<\/code> and <code>DynamicDataAttribute<\/code> have an <code>IgnoreMessage<\/code> property. When it&#8217;s set to non-null string, the source will be ignored. We are also allowing any custom <code>ITestDataSource<\/code> to have a similar behavior by simply implementing the <code>ITestDataSourceIgnoreCapability<\/code>. With this change, you can now do:<\/p>\n<pre><code class=\"language-csharp\">[TestMethod]\n[DataRow(0)]\n[DataRow(1, IgnoreMessage = \"This row is ignored\")]\n[DataRow(2)]\npublic void TestMethod1(int x)\n{\n    Assert.AreNotEqual(1, x);\n}<\/code><\/pre>\n<p>or<\/p>\n<pre><code class=\"language-csharp\">[TestMethod]\n[DynamicData(nameof(Data1))]\n[DynamicData(nameof(Data2), IgnoreMessage = \"This dynamic data source is ignored\")]\n[DynamicData(nameof(Data3))]\npublic void TestMethod1(int x)\n{\n    Assert.IsTrue(x &lt; 10);\n}\n\n\/\/ 0 to 3 - passes\npublic static IEnumerable&lt;int&gt; Data1 =&gt; Enumerable.Range(0, 4);\n\n\/\/ 50 to 59 - ignored, and will fail if not ignored\npublic static IEnumerable&lt;int&gt; Data2 =&gt; Enumerable.Range(50, 10);\n\n\/\/ 4 to 9 - passes\npublic static IEnumerable&lt;int&gt; Data3 =&gt; Enumerable.Range(4, 6);<\/code><\/pre>\n<p>Finally, we have introduced <code>TestDataRow&lt;T&gt;<\/code> allowing users to wrap their data in a customizable container where you can set the display name, ignore the entry and much more to come. Any <code>ITestDataSource<\/code> can now return <code>IEnumerable&lt;TestDataRow&lt;T&gt;&gt;<\/code>. For example:<\/p>\n<pre><code class=\"language-csharp\">    [TestMethod]\n    [DynamicData(nameof(GetData))]\n    public void TestMethod1(int num1, int num2, int expectedSum)\n    {\n        Assert.AreEqual(expectedSum, num1 + num2);\n    }\n\n    private static IEnumerable&lt;TestDataRow&lt;(int Num1, int Num2, int ExpectedSum)&gt;&gt; GetData()\n    {\n        yield return new((1, 2, 3));\n        yield return new((2, 3, 5));\n        yield return new((3, 4, 7)) { DisplayName = \"This test case has a special display name\" };\n        yield return new((3, 3, 7)) { IgnoreMessage = \"Ignored because of ...\" };\n    }<\/code><\/pre>\n<h3>Retrying flaky tests<\/h3>\n<p>We&#8217;re excited to introduce RetryAttribute, a powerful new feature that lets you automatically retry failing test methods! With just one attribute, you can configure <code>MaxRetryAttempts<\/code>, <code>MillisecondsDelayBetweenRetries<\/code>, and <code>BackoffType<\/code> (choosing between <code>Constant<\/code> and <code>Exponential<\/code> strategies for smarter retries). For example:<\/p>\n<pre><code class=\"language-csharp\">[TestMethod]\n[Retry(3, MillisecondsDelayBetweenRetries = 100, BackoffType = DelayBackoffType.Constant)]\npublic void TestMethod()\n{\n    Assert.Fail(\"Failing test\");\n}<\/code><\/pre>\n<p>This test method will run four times (the original run and 3 retries), with a time delay of 100 milliseconds between each run.<\/p>\n<p>Need more flexibility? You can create your own custom retry logic by inheriting from <code>RetryBaseAttribute<\/code>, the same foundation as the <code>RetryAttribute<\/code>.<\/p>\n<p>If you are using MTP, enabling retries across your entire test suite is even easier, just leverage the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/unit-testing-platform-extensions-policy#retry\">retry extension<\/a> and let your tests recover automatically!<\/p>\n<h3>Introducing conditional tests<\/h3>\n<p>MSTest now includes <code>OSConditionAttribute<\/code>, giving you fine-grained control over where your tests run. This attribute lets you specify which operating systems a test <em>should<\/em> or <em>should not<\/em> execute on, making it easier to handle OS-specific behavior in your test suite.<\/p>\n<p>Here are some examples of this attribute:<\/p>\n<pre><code class=\"language-csharp\">[TestClass]\npublic sealed class MyTestClass\n{\n    [TestMethod]\n    [OSCondition(OperatingSystems.Windows)]\n    public void TestMethodRunningOnlyOnWindows()\n    {\n    }\n\n    [TestMethod]\n    [OSCondition(OperatingSystems.Linux | OperatingSystems.OSX)]\n    public void TestMethodRunningOnlyOnLinuxAndMac()\n    {\n    }\n\n    [OSCondition(ConditionMode.Exclude, OperatingSystems.Windows)]\n    public void TestMethodRunningOnAllOSesButWindows()\n    {\n    }\n}<\/code><\/pre>\n<p>As part of the development of this feature, we are providing the <code>ConditionBaseAttribute<\/code> abstract class as parent of <code>OSConditionAttribute<\/code> and <code>IgnoreAttribute<\/code>. You can use this new attribute as the root of any conditional feature you would like to implement. We have many ideas for extra conditional features we could provide, and we would really appreciate your input in prioritizing the ones that would be the most useful to you!<\/p>\n<h3>Analyzers<\/h3>\n<p>The <code>MSTestAnalysisMode<\/code> feature allows you to control the severity levels of code analyzers within your test projects. By setting the <code>MSTestAnalysisMode<\/code> MSBuild property, you can determine which analyzers are enabled and their corresponding severity levels.<\/p>\n<p>Available Modes:<\/p>\n<ul>\n<li><code>None<\/code>: Disables all analyzers. You can enable individual analyzers using <code>.editorconfig<\/code> or <code>.globalconfig<\/code> files.<\/li>\n<li><code>Default<\/code>: Follows the default behavior for each rule. Rules enabled by default will use their default severity, while those disabled by default will have a severity of none.<\/li>\n<li><code>Recommended<\/code>: Elevates rules enabled by default with an Info (suggestion) severity to warnings. Certain rules may be escalated to errors in both <code>Recommended<\/code> and <code>All<\/code> modes.For example, <code>MSTEST0003: Test methods should have valid layout<\/code> is escalated to an error in these modes.<\/li>\n<li><code>All<\/code>: More aggressive than <code>Recommended<\/code>. All rules are enabled as warnings, with certain rules escalating to errors.<\/li>\n<\/ul>\n<p>Usage Example:<\/p>\n<p>To set the <code>MSTestAnalysisMode<\/code> to <code>Recommended<\/code>, include the following in your project file or in <em>Directory.Build.props<\/em>:<\/p>\n<pre><code class=\"language-xml\">&lt;PropertyGroup&gt;\n  &lt;MSTestAnalysisMode&gt;Recommended&lt;\/MSTestAnalysisMode&gt;\n&lt;\/PropertyGroup&gt;<\/code><\/pre>\n<p>This configuration ensures that your test projects adhere to best practices by appropriately adjusting the severity levels of various analyzers.<\/p>\n<p>Note that using <em>Directory.Build.props<\/em> is more recommended than project file, as it ensures the property applies to all your test projects and you add it in one place instead of multiple project files. It&#8217;s also safe if <code>MSTestAnalysisMode<\/code> is defined for non-test projects via <em>Directory.Build.props<\/em>. It only affects MSTest projects.<\/p>\n<p>For more detailed information, refer to the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/mstest-analyzers\/overview#mstestanalysismode\">MSTest code analysis documentation<\/a>.<\/p>\n<p>Let&#8217;s now focus on 2 newly added analyzers that have shown promising results during internal dogfood:<\/p>\n<ul>\n<li>MSTEST0038: Don&#8217;t use &#8216;Assert.AreSame&#8217; or &#8216;Assert.AreNotSame&#8217; with value types<\/li>\n<li>MSTEST0040: Do not assert inside &#8216;async void&#8217; contexts<\/li>\n<\/ul>\n<p>MSTEST0038 warns when the methods <code>Assert.AreSame<\/code> and <code>Assert.AreNotSame<\/code> are called with a value type argument. These methods work by comparing references, making its usage with value types incorrect as it yields unexpected results.<\/p>\n<p>The following use of <code>Assert.AreSame<\/code> will always fail:<\/p>\n<pre><code class=\"language-csharp\">Assert.AreSame(0, 0);<\/code><\/pre>\n<p>This is because we pass them to <code>ReferenceEquals<\/code>, which accepts <code>object<\/code>. So, each argument will be boxed into a different object, and they will not have equal references.<\/p>\n<p>The same applies to <code>Assert.AreNotSame<\/code>, but is more critical.<\/p>\n<pre><code class=\"language-csharp\">Assert.AreNotSame(0, 0);<\/code><\/pre>\n<p>By applying the same logic, the above assert will always pass. This can hide real production bugs as you may not be asserting what you want.<\/p>\n<p>Note that the above examples are for demonstration purposes only and are not real-world examples. A more realistic example can be:<\/p>\n<pre><code class=\"language-csharp\">\/\/ This will still pass even if the array has a single element.\nAssert.AreNotSame(1, myArray.Length);<\/code><\/pre>\n<p>For more information, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/mstest-analyzers\/mstest0038\">MSTEST0038: Don&#8217;t use &#8216;Assert.AreSame&#8217; or &#8216;Assert.AreNotSame&#8217; with value types<\/a>.<\/p>\n<p>MSTEST0040 warns for usage of assertion APIs in async void. Depending on whether you use a custom <code>SynchronizationContext<\/code> and whether you use VSTest under .NET Framework, an exception thrown in <code>async void<\/code> context can either be swallowed completely or can crash the test host. For more information, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/mstest-analyzers\/mstest0040\">MSTEST0040: Do not assert inside &#8216;async void&#8217; contexts<\/a>.<\/p>\n<h3>Wrapping up<\/h3>\n<p>With MSTest 3.8, we&#8217;re continuing our mission to make testing in .NET easier, more powerful, and more enjoyable. This release brings highly requested features, reduces tedious tasks, and enhances the overall developer experience \u2014 allowing you to focus on writing great tests instead of wrestling with infrastructure.<\/p>\n<p>We hope these improvements help streamline your workflow and showcase our commitment to listening to your feedback and evolving MSTest to meet your needs. As always, we&#8217;d love to hear your thoughts \u2014 <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/unit-testing-mstest-getting-started\">try out MSTest 3.8<\/a> and let us know what you think by opening an issue or a discussion on <a href=\"https:\/\/github.com\/microsoft\/testfx\/\">microsoft\/testfx GitHub repository<\/a>!<\/p>\n<p><div  class=\"d-flex justify-content-left\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/unit-testing-mstest-getting-started\" target=\"_blank\">Try MSTest 3.8 Today<\/a><\/div><\/p>\n<p>A huge shoutout to <a href=\"https:\/\/github.com\/SimonCropp\">@SimonCropp<\/a> for his incredible contributions to MTP and MSTest! His dedication and expertise continue to make a lasting impact, and this release is no exception. We&#8217;re grateful for his ongoing support and invaluable efforts in helping shape a better testing experience for the entire .NET community. Thank you, Simon!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MSTest 3.8 is here! It&#8217;s built on your feedback and packed with powerful new features to simplify and smooth your testing experience.<\/p>\n","protected":false},"author":181791,"featured_media":55865,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756,636,7199],"tags":[4,7265,46,73,136,145],"class_list":["post-55864","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","category-fsharp","category-visual-basic","tag-net","tag-announcements","tag-c","tag-f","tag-testing","tag-visual-basic"],"acf":[],"blog_post_summary":"<p>MSTest 3.8 is here! It&#8217;s built on your feedback and packed with powerful new features to simplify and smooth your testing experience.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55864","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\/181791"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=55864"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55864\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/55865"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=55864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}