{"id":33506,"date":"2017-06-13T21:23:21","date_gmt":"2017-06-14T04:23:21","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/devops\/?p=33506"},"modified":"2019-02-14T15:51:34","modified_gmt":"2019-02-14T23:51:34","slug":"accelerated-continuous-testing-with-test-impact-analysis-part-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/accelerated-continuous-testing-with-test-impact-analysis-part-3\/","title":{"rendered":"Accelerated Continuous Testing with Test Impact Analysis &#8211; Part 3"},"content":{"rendered":"<p>At its core,\u00a0TIA collects, and subsequently consults, a map of the dynamic dependencies of each test method as it is executing. As the test method is executing it will cover various methods\u00a0&#8211; the source file in which those methods reside are the dynamic dependencies that get tracked. So, the mapping ends up like the following:<\/p>\n<pre>    TestMethod1\n        a.cs\n        b.cs\n        d.cs\n    TestMethod2\n        a.cs\n        k.cs\n        z.cs<\/pre>\n<p>and so on \u2026\nLater, when a commit comes in containing say <em>a.cs<\/em>, TIA consults the mapping and runs only the test methods that had a.cs as their dynamic dependency. It of course takes care of newly introduced test methods (that might come in as part of the commit), and carries forward previously failing test methods as well.<\/p>\n<p>TIA knows about <em>.cs<\/em> and <em>.vb<\/em> files and treats all other file types as &#8220;unknown&#8221; file types. If the commit were to contain any unknown file type, TIA will fall back to running all tests; this is TIA\u2019s fallback logic.<\/p>\n<p><strong>Striking a balance<\/strong>\nFrom the perspective of a test selection system, the following properties need to be balanced:<\/p>\n<ol>\n<li>it should be <em>efficient<\/em> at selecting the relevant tests<\/li>\n<li>it should be <em>precise<\/em> in that tests that are not affected ought not be selected, and<\/li>\n<li>it should be <em>safe<\/em> in that tests that are not selected ought not to be affected by changes.<\/li>\n<\/ol>\n<p>Consider a test selection system that might use block level code coverage data gathered from a previous run to predict tests that are likely to be impacted by code changes made in the current run. While this can be precise about identifying tests, it can be unsafe: any code change not related to a method body might get ignored (e.g., annotations using attributes, etc.). Also, changes like adding a method, removing a method, or overriding a method might go undetected. It might also not address any issues such as library updates, etc.<\/p>\n<p>TIA, on the other hand, tracks dynamic dependencies at the file level.\nThus, it might select a few more tests. This make it less precise. However, the approach accounts for changes not related to a method body (annotations, etc.). in conjunction with its fallback logic, this makes it more safe. By collecting dependencies at file level granularity TIA has less data to record, keep updated, and manage. This makes it efficient.<\/p>\n<p><strong>Controlling TIA<\/strong>\nThere are multiple ways to condition TIA\u2019s behaviour.<\/p>\n<ol>\n<li>Through the VSTest task\u2019s UI, TIA can be conditioned to run &#8220;all&#8221; tests at a configured periodicity. Setting this is recommended, and is the means to regulate TIA&#8217;s test selection.<\/li>\n<li>Even after TIA has been enabled in the VSTest task, it can be disabled for a particular build by setting the build variable <code>DisableTestImpactAnalysis<\/code> to <code>true<\/code>. This override will force TIA to run all tests for that build. In subsequent builds, TIA will go back to optimized test selection.<\/li>\n<\/ol>\n<p>As already mentioned when TIA opens up a commit and sees an unknown file type, it falls back to running all tests. While this is good from a safety perspective, tuning this behaviour might be useful in some cases. Here are a couple:<\/p>\n<ol>\n<li>Consider a single repo containing projects from multiple teams (perhaps belonging to a single product family). A team might have a build definition that gets triggered when a commit happens anywhere within the repo, and then run tests only belonging to the team. In this case, TIA\u2019s fallback logic can be conditioned to trigger only if unknown files are committed within certain paths in the repo by setting the <code>TIA_IncludePathFilters<\/code> to only the relevant paths within the repo using the <a href=\"https:\/\/www.visualstudio.com\/en-us\/docs\/build\/steps\/file-matching-patterns\">minimatch<\/a> pattern. Multiple values can be provided using the semicolon as a separator. By default, there are no path filters explicitly configured, and there is an implicit include of all files in the repository. As soon as you specify an explicit path filter the implicit include of the entire repo no longer applies to the trigger.<\/li>\n<li>Consider changes happening to certain file types that you know do not influence the outcome of tests &#8211; say commits happening to some markdown files, .csproj files, etc. In this case TIA can be conditioned to ignore changes to certain file types. For instance, if you want the TIA engine to ignore changes to .csproj files, set the build variable <code>TIA_IncludePathFilters<\/code> to the value <code>\"!**\\*.csproj\"<\/code>. The value is specified using the <a href=\"https:\/\/www.visualstudio.com\/en-us\/docs\/build\/steps\/file-matching-patterns\">minimatch<\/a> pattern, and multiple values can be provided using the semicolon as a separator.<\/li>\n<\/ol>\n<p><strong>Trusting TIA<\/strong>\nTo use TIA, one needs to trust it. Here are a couple of ways to validate and grow trust on TIA&#8217;s <a href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2017\/03\/02\/accelerated-continuous-testing-with-test-impact-analysis-part-1\/\">robust test selection<\/a>.<\/p>\n<ol>\n<li>Manually validate the selection &#8211; as the developer who knows how the SUT and tests are architected, one could manually validate that TIA indeed selects the right set of tests (the test results page will list the tests that were run, and the tests that were not impacted).<\/li>\n<li>Run TIA selected tests and &#8220;all&#8221; tests in sequence &#8211; in a build definition, have 2 tests tasks: run impacted Tests (T1), and run \u201call\u201d tests (T2). If T1 passes, notice that T2 passes as well. If there was a failing test in T1, notice that T2 reports the same set of failures.<\/li>\n<\/ol>\n<p><strong>Enable TIA in your PR workflow<\/strong>\nYou no longer need to sacrifice the comprehensiveness of your test suite just so that your CI definitions complete fast. Go ahead and &#8220;Shift Left&#8221; and have a comprehensive test suite that can catch integration errors as soon as possible, and have them run as part of the PR build\u00a0&#8211; let TIA&#8217;s test\u00a0selection take care of the <a href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2017\/05\/16\/accelerated-continuous-testing-with-test-impact-analysis-part-2\/\">performance<\/a>.<\/p>\n<p>Enable TIA in your PR workflow. Commit with confidence!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>At its core,\u00a0TIA collects, and subsequently consults, a map of the dynamic dependencies of each test method as it is executing. As the test method is executing it will cover various methods\u00a0&#8211; the source file in which those methods reside are the dynamic dependencies that get tracked. So, the mapping ends up like the following: [&hellip;]<\/p>\n","protected":false},"author":765,"featured_media":45953,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,252],"tags":[],"class_list":["post-33506","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-testing"],"acf":[],"blog_post_summary":"<p>At its core,\u00a0TIA collects, and subsequently consults, a map of the dynamic dependencies of each test method as it is executing. As the test method is executing it will cover various methods\u00a0&#8211; the source file in which those methods reside are the dynamic dependencies that get tracked. So, the mapping ends up like the following: [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/33506","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/765"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=33506"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/33506\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/45953"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=33506"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=33506"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=33506"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}