{"id":4244,"date":"2025-09-29T15:43:05","date_gmt":"2025-09-29T19:43:05","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=4244"},"modified":"2025-09-29T15:43:05","modified_gmt":"2025-09-29T19:43:05","slug":"tldr-too-long-didnt-run-part-4-incremental-testing-in-ci","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2025\/09\/29\/tldr-too-long-didnt-run-part-4-incremental-testing-in-ci\/","title":{"rendered":"TL;DR: Too Long; Didn\u2019t Run: Part 4 \u2013 Incremental Testing in CI"},"content":{"rendered":"<div class=\"content\"><!--introduction-->\r\n<p>In the <a href=\"https:\/\/blogs.mathworks.com\/developer\/2025\/08\/20\/tldr-too-long-didnt-run-part-3-incremental-testing-with-the-matlab-build-tool\/\">previous post<\/a> we explored how to use the MATLAB build tool to run only impacted tests during local, iterative development cycles. In this post, we'll take that concept further and apply incremental testing in a continuous integration (CI) environment to speed up feedback and improve overall throughput.<\/p>\r\n<!--\/introduction-->\r\n<h2>Contents<\/h2>\r\n<div>\r\n<ul>\r\n<li>\r\n<a href=\"#ebf16b03-f3ac-4771-999f-8258cf0bf75a\">Example Project: <i>arithmetic<\/i><\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#e83568d7-56cb-48a7-9112-b1c4c979a00e\">Context<\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#d80cebc8-1927-4b75-ba01-eb8a1e5a9630\">The Build File<\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#6ad55d00-b3ec-4bf2-9647-0f6c263054c5\">CI Pipeline Configuration<\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#f74199df-1b88-491d-827d-edee2b1665ba\">First Run (No Cache)<\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#502c7c39-045b-4d2a-b269-2eba77a03038\">Second Run: Modify <tt>h_my_add.m<\/tt><\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#7d8121b9-4ce3-41bd-8881-5ac847978938\">Third Run: Modify <tt>my_subtract.m<\/tt><\/a>\r\n<\/li>\r\n<li>\r\n<a href=\"#f14363e2-509f-4460-be96-608c0c88eafe\">Wrapping Up<\/a>\r\n<\/li>\r\n<\/ul>\r\n<\/div>\r\n<h2>Example Project: <i>arithmetic<\/i><a name=\"ebf16b03-f3ac-4771-999f-8258cf0bf75a\"><\/a>\r\n<\/h2>\r\n<div style=\"border-left: 4px solid #007acc; background-color: #f0f8ff; padding: 12px 16px; margin: 16px 0; font-family: sans-serif;\"><strong>Tip:<\/strong>To follow along, you can access the supporting files for this post in the <a href=\"https:\/\/github.com\/mathworks\/developer-zone-blog\/tree\/2025-Test-Impact-Analysis-CI\">Developer Zone blog GitHub repository<\/a>. <\/div>\r\n<p>For this walkthrough, we'll use a simple example project, <i>arithmetic<\/i>, that contains:<\/p>\r\n<div>\r\n<ul>\r\n<li>MATLAB functions for basic arithmetic operations.<\/li>\r\n<li>Unit tests for those functions.<\/li>\r\n<\/ul>\r\n<\/div>\r\n<p>\r\n<b>Project Structure:<\/b>\r\n<\/p>\r\n<p>\r\n<img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-project-structure.png\" alt=\"\"> <\/p>\r\n<\/p>\r\n<p>The <tt>toolbox<\/tt> folder holds the source code, while the <tt>tests<\/tt> folder contains tests for those functions.<\/p>\r\n<h2>Context<a name=\"e83568d7-56cb-48a7-9112-b1c4c979a00e\"><\/a>\r\n<\/h2>\r\n<p>Whether (and when) you choose to run only impacted tests versus the full test suite depends on many factors, including:<\/p>\r\n<div>\r\n<ul>\r\n<li>Project complexity<\/li>\r\n<li>Branching strategy<\/li>\r\n<li>Release cadence and workflow<\/li>\r\n<li>Team or organizational standards<\/li>\r\n<\/ul>\r\n<\/div>\r\n<p>This post does not prescribe a software qualification strategy. Instead, it shows an example of how to achieve incremental testing in CI using the build tool's <i>test impact analysis<\/i>.<\/p>\r\n<p>For this example, we'll configure the build to run incremental tests on <b>feature branches<\/b> whenever a developer pushes changes. The same approach, however, can be adapted to other events such as pull requests, merges to <tt>main<\/tt> , or nightly builds.<\/p>\r\n<p>I'll demonstrate this example using <b>GitHub Actions<\/b>, but this pattern is easily transferable to other CI systems like GitLab CI, Azure DevOps or Jenkins.<\/p>\r\n<h2>The Build File<a name=\"d80cebc8-1927-4b75-ba01-eb8a1e5a9630\"><\/a>\r\n<\/h2>\r\n<p>The <tt>buildfile.m<\/tt> in the project defines a build plan with a <tt>test<\/tt> task that runs the project's tests and produces results.<\/p>\r\n<pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> plan = buildfile\r\nimport<span class=\"string\"> matlab.buildtool.tasks.*<\/span>\r\n\r\nplan = buildplan(localfunctions);\r\n\r\nplan(<span class=\"string\">\"clean\"<\/span>) = CleanTask;\r\nplan(<span class=\"string\">\"test\"<\/span>) = TestTask(SourceFiles=<span class=\"string\">\"toolbox\"<\/span>,TestResults=<span class=\"string\">\"results\/test-results.xml\"<\/span>);\r\n\r\nplan.DefaultTasks=<span class=\"string\">\"test\"<\/span>;\r\n\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre>\r\n<p>Incremental testing is enabled by setting <tt>RunOnlyImpactedTests<\/tt> property or task argument to true on a TestTask instance. In this example, we will use the task argument at runtime, rather than baking it directly into the <tt>test<\/tt> task configuration.<\/p>\r\n<p>As discussed in the <a href=\"https:\/\/blogs.mathworks.com\/developer\/2025\/08\/20\/tldr-too-long-didnt-run-part-3-incremental-testing-with-the-matlab-build-tool\/\">previous post<\/a>, the build tool stores task traces in the <tt>.buildtool<\/tt> cache folder. A <b>task trace<\/b> is a MATLAB release-specific record of a task's inputs, outputs, actions and arguments from its last successful run.<\/p>\r\n<p>By caching and restoring these task traces (and outputs such as test results) appropriately in CI, we can effectively enable the build tool to detect changes and run only impacted tests in CI workflows as well.<\/p>\r\n<h2>CI Pipeline Configuration<a name=\"6ad55d00-b3ec-4bf2-9647-0f6c263054c5\"><\/a>\r\n<\/h2>\r\n<p>The GitHub Actions workflow configuration is defined in <tt>.github\/workflows\/ci.yml<\/tt>:<\/p>\r\n<pre>\r\nname: MATLAB Build\r\n\r\non:\r\n  push:\r\n    branches:\r\n      - 'feature\/*'    # Only run on feature branches\r\n\r\n  workflow_dispatch:\r\n\r\njobs:\r\n  build:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      # Check out the repository\r\n      - uses: actions\/checkout@v4\r\n      \r\n      # Sets up MATLAB on a GitHub-hosted runner\r\n      - name: Set up MATLAB\r\n        uses: matlab-actions\/setup-matlab@v2\r\n        with:\r\n          release: R2025a\r\n          products: &gt;\r\n            MATLAB\r\n            MATLAB_Test\r\n\r\n      # Cache .buildtool and test results\r\n      - name: Cache buildtool artifacts\r\n        id: buildtool-cache\r\n        uses: actions\/cache@v4\r\n        with:\r\n            path: |\r\n                .buildtool\r\n                results\r\n            key: ${{ runner.os }}-buildtool-cache-${{ github.ref_name }}\r\n\r\n      # Run MATLAB build\r\n      - name: Run build\r\n        uses: matlab-actions\/run-build@v2\r\n        with:\r\n          tasks: test(RunOnlyImpactedTests=1)\r\n          build-options: -verbosity 3\r\n<\/pre>\r\n<p>The configuration has the following key steps:<\/p>\r\n<div>\r\n<ul>\r\n<li><strong>Setup MATLAB<\/strong>\r\n<p>Installs MATLAB R2025a including MATLAB Test using the MathWorks' official <a href=\"https:\/\/github.com\/matlab-actions\/setup-matlab\">matlab-actions\/setup-matlab<\/a> action. We are using GitHub-hosted runners in this demo.<\/p>\r\n<\/li>\r\n<\/ul>\r\n<\/div>\r\n<div>\r\n<ul>\r\n<li><strong>Setup Caching<\/strong>\r\n<p>Cache and restore the <tt>.buildtool<\/tt> folder and <tt>results\/<\/tt> directory. The cache key, in this example, includes the OS and branch name (e.g., Linux-buildtool-cache-feature\/cache). See <a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/dependency-caching#cache-action-usage\">GitHub Actions cache action usage<\/a> for more information on how to use cache action and how key matching works.<\/p><\/li>\r\n<\/ul>\r\n<\/div>\r\n<div>\r\n<ul>\r\n<li><strong>Run MATLAB<\/strong>\r\n<p>Uses <a href=\"https:\/\/github.com\/matlab-actions\/run-build\">matlab-actions\/run-build<\/a> to execute the test task with RunOnlyImpactedTests=true to run only impacted tests.<\/p><\/li>\r\n<\/ul>\r\n<\/div>\r\n<h2>First Run (No Cache)<a name=\"f74199df-1b88-491d-827d-edee2b1665ba\"><\/a>\r\n<\/h2>\r\n<p>Let's create a feature branch <tt>feature\/cache<\/tt> under this blog's branch <tt>2025-Test-Impact-Analysis-CI<\/tt>.<\/p>\r\n<p>On the first pipeline run, there is no cache hit for key <tt>Linux-buildtool-cache-feature\/cache<\/tt>.<\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-first-run-cache-miss.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<p>All tests run because no prior traces exist: <\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-first-run-all-tests.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<\/p>\r\n<p>The pipeline then creates and saves a cache for the <strong>feature\/cache<\/strong> branch for future runs:<\/p>\r\n<p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-first-run-cache.png\" alt=\"\" width=\"480\" height=\"120\"> <\/p>\r\n<\/p>\r\n<h2>Second Run: Modify <tt>h_my_add.m<\/tt><a name=\"502c7c39-045b-4d2a-b269-2eba77a03038\"><\/a>\r\n<\/h2>\r\n<p>Let's make a change.<\/p>\r\n<div>\r\n<ul>\r\n<li>Clone the repository at branch <tt>2025-Test-Impact-Analysis-CI<\/tt>.<\/li>\r\n<li>Run the build locally within MATLAB using <tt>buildtool<\/tt>. All the tests will run since there are no task traces yet.<\/li>\r\n<\/ul>\r\n<\/div>\r\n<p>Next, checkout and switch to the <tt>feature\/cache<\/tt> branch, then modify <tt>h_my_add.m<\/tt>, say, by adding an arguments block:<\/p>\r\n<pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> out = h_my_add(in1, in2)\r\n<span class=\"comment\">% This is a helper function to add 2 values<\/span>\r\n\r\n<span class=\"keyword\">arguments<\/span> \r\n     in1 {mustBeNumeric}\r\n     in2 {mustBeNumeric}\r\n<span class=\"keyword\">end<\/span>\r\n\r\nout = in1 + in2;\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre>\r\n<p>Verify locally with:<\/p>\r\n<pre class=\"language-matlab\">&gt;&gt; buildtool test(RunOnlyImpactedTests=1) -verbosity 3\r\n<\/pre>\r\n<p>\r\n<div style=\"border-left: 4px solid #007acc; background-color: #f0f8ff; padding: 12px 16px; margin: 16px 0; font-family: sans-serif;\"><strong>Tip: <\/strong>Running the build at verbosity 3 or higher shows you what exactly has changed and why the task is re-running.<\/div>\r\n<p>\r\n<b>Output (excerpt)<\/b> :<\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-second-run-local.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<p>Only 8 out of 21 tests were selected to run&mdash;those impacted by the modified file. That's promising!<\/p>\r\n<p>Now let's push the changes and inspect the CI pipeline.<\/p>\r\n<p>We see a cache hit and it confirms the <tt>.buildtool<\/tt> and <tt>results<\/tt> folder were restored.<\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-feature-cache-hit.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<p>The build tool leverages the restored cache to run only impacted tests.<\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-feature-cache-run-build.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<h2>Third Run: Modify <tt>my_subtract.m<\/tt><a name=\"7d8121b9-4ce3-41bd-8881-5ac847978938\"><\/a>\r\n<\/h2>\r\n<p>Make another change, commit, and push. The cache is restored again, and now only the tests impacted by both outstanding changes on the feature branch are executed:<\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-third-run-cache-hit.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<p>\r\n<img decoding=\"async\" loading=\"lazy\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-third-run-impacted-tests.png\" alt=\"\" width=\"640\" height=\"180\"> <\/p>\r\n<h2>Wrapping Up<a name=\"f14363e2-509f-4460-be96-608c0c88eafe\"><\/a>\r\n<\/h2>\r\n<p>There you have it! This workflow demonstrates how to combine: \r\n<div>\r\n<ul>\r\n<li>MATLAB build tool's test impact analysis, and<\/li>\r\n<li>CI platform's caching<\/p><\/li>\r\n<\/ul>\r\n<\/div>\r\n<p>... to achieve incremental testing in CI.<\/p>\r\n<p>You can refine this pattern by adding things like: \r\n<div>\r\n<ul>\r\n<li>Adding cache restore keys to support fallback scenarios.<\/li> \r\n<li>Adjusting cache keys for multi-platform or multi-branch setups.<\/li> \r\n<li>Deciding when to run incremental tests vs. full regression tests.<\/li>\r\n<\/p>\r\n<\/ul>\r\n<\/div>\r\n<p><em>Would you run incremental testing in your CI pipeline? At what point in your release cycle do you feel most comfortable running only impacted tests?<\/em><\/p>\r\n<hr>\r\n<p>\r\nWe envision continuing to invest in making incremental testing better across both local development and CI workflows. Your feedback is essential in helping us focus on the improvements that matter most to you. So, leave us your thoughts in the comments section below.\r\n<\/p>\r\n<p>\r\nThat\u2019s a wrap (for now) on our series about Test Impact Analysis. Stay tuned \u2014 we have more software development topics in the pipeline, and we look forward to sharing them with you!\r\n<\/p>\r\n<script language=\"JavaScript\"> <!-- \r\n    function grabCode_505b7ee61872420f9dcd2b3a2f0c5c47() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='505b7ee61872420f9dcd2b3a2f0c5c47 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 505b7ee61872420f9dcd2b3a2f0c5c47';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2025 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script>\r\n<p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\">\r\n<br>\r\n<a href=\"javascript:grabCode_505b7ee61872420f9dcd2b3a2f0c5c47()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript>\r\n<\/span><\/a>\r\n<br>\r\n<br>\r\n      Published with MATLAB&reg; R2025a<br>\r\n<\/p>\r\n<\/div>\r\n<!--\r\n505b7ee61872420f9dcd2b3a2f0c5c47 ##### SOURCE BEGIN #####\r\n%% \r\n% In the <https:\/\/blogs.mathworks.com\/developer\/2025\/08\/20\/tldr-too-long-didnt-run-part-3-incremental-testing-with-the-matlab-build-tool\/ previous post> we explored how to use the MATLAB build tool to run only impacted tests\r\n% during local, iterative development cycles. In this post, we'll take that\r\n% concept further and apply incremental testing in a continuous integration\r\n% (CI) environment to speed up feedback and improve overall throughput.\r\n% \r\n%% Example Project: _arithmetic_ \r\n% For this walkthrough, we'll use a simple example project,\r\n% _arithmetic_, that contains:\r\n% \r\n% * MATLAB functions for basic arithmetic operations.\r\n% * Unit tests for those functions. \r\n%\r\n% *Project Structure:* \r\n%\r\n%   PROJECT_ROOT\r\n%       \u2502   arithmetic.prj\r\n%       \u2502   buildfile.m\r\n%       \u2502\r\n%       \u251c\u2500\u2500\u2500.github\r\n%       \u2502   \u2514\u2500\u2500\u2500workflows\r\n%       \u2502           ci.yml\r\n%       \u2502\r\n%       \u251c\u2500\u2500\u2500tests\r\n%       \u2502       MyAddTest.m\r\n%       \u2502       MyCalculateTest.m\r\n%       \u2502       MyDivideTest.m\r\n%       \u2502       MyMultiplyTest.m\r\n%       \u2502       MySubtractTest.m\r\n%       \u2502\r\n%       \u2514\u2500\u2500\u2500toolbox\r\n%               h_my_add.m\r\n%               my_add.m\r\n%               my_calculate.m\r\n%               my_divide.m\r\n%               my_multiply.m\r\n%               my_subtract.m\r\n%\r\n% The |toolbox| folder holds the source code, while the |tests| folder \r\n% contains tests for those functions.\r\n% \r\n%% Context\r\n% Whether (and when) you choose to run only impacted tests versus the full\r\n% test suite depends on many factors, including:\r\n% \r\n% * Project complexity\r\n% * Branching strategy\r\n% * Release cadence and workflow\r\n% * Team or organizational standards\r\n%\r\n% This post does not prescribe a software qualification strategy. Instead,\r\n% it shows an example on how to achieve incremental testing in CI using the\r\n% build tool's _test impact analysis_.\r\n% \r\n% For this example, we'll configure the build to run incremental tests on\r\n% *feature branches* whenever a developer pushes changes. The same\r\n% approach, however, can be adapted to other events such as pull requests,\r\n% merges to |main| , or nightly builds.\r\n% \r\n% I'll demonstrate this example using *GitHub Actions*, but this pattern is\r\n% easily transferable to other CI systems like GitLab CI, Azure DevOps or\r\n% Jenkins.\r\n%\r\n%% The Build File\r\n% The |buildfile.m| in the project defines a build plan with a |test| task\r\n% that runs the project's tests and produces results.\r\n% \r\n% <include>buildfile.m<\/include>\r\n%\r\n% Incremental testing is enabled by setting |RunOnlyImpactedTests| property\r\n% or task argument to true on a TestTask instance. In this example, we will\r\n% use the task argument at runtime, rather than baking it directly into the\r\n% |test| task configuration.\r\n% \r\n% As discussed in the previous post, the build tool stores task traces in\r\n% the |.buildtool| cache folder. A *task trace* is a MATLAB\r\n% release-specific record of a task's inputs, outputs, actions and\r\n% arguments from its last successful run.\r\n% \r\n% By caching and restoring these task traces (and outputs such as test\r\n% results) in CI, we can effectively enable the build tool to detect changes\r\n% and run only impacted tests in CI workflows as well. \r\n% \r\n%% CI Pipeline Configuration\r\n% The GitHub Actions workflow configuration is defined in\r\n% |.github\/workflows\/ci.yml|:\r\n% \r\n% <include>.github\/workflows\/ci.yml<\/include>\r\n% \r\n% The configuration has the following key steps:\r\n% \r\n% * Setup MATLAB\r\n% Installs MATLAB R2025a including MATLAB Test using the MathWorks'\r\n% official <https:\/\/github.com\/matlab-actions\/setup-matlab matlab-actions\/setup-matlab> action. \r\n% \r\n% * Setup Caching \r\n% Cache and restore the |.buildtool| folder and |results\/| directory. The\r\n% cache key, in this example, includes the OS and branch name (e.g.,\r\n% Linux-buildtool-cache-feature\/cache). See\r\n% <https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/dependency-caching#cache-action-usage\r\n% GitHub Actions cache action usage> for more information on how to use\r\n% cache action and how key matching works.\r\n% \r\n% * Run MATLAB\r\n% Uses <https:\/\/github.com\/matlab-actions\/run-build\r\n% matlab-actions\/run-build> to execute the test task with\r\n% RunOnlyImpactedTests=true to run only impacted tests. \r\n% \r\n%% First Run (No Cache)\r\n% Let's create a feature branch |feature\/cache| under this blog's branch\r\n% |2025-Test-Impact-Analysis-CI|. \r\n% \r\n% On the first pipeline run, there is no cache hit for key\r\n% |Linux-buildtool-cache-feature\/cache|.\r\n% \r\n% <<2025-tia-ci-first-run-cache-miss.png>>\r\n% \r\n% All tests run because no prior traces exist:\r\n% <<2025-tia-ci-first-run-all-tests.png>>\r\n% \r\n% The pipeline then creates and saves a cache for the |feature\/cache| branch for future runs:\r\n% <<2025-tia-ci-first-run-cache.png>>\r\n% \r\n% <<created_cache>>\r\n% \r\n%% Second Run: Modify |h_my_add.m|\r\n% Let's make a change. \r\n%\r\n% * Clone the repository at branch |2025-Test-Impact-Analysis-CI|. \r\n% * Run the build locally with |buildtool|. \r\n% ** On the first run, all the tests run (no task traces yet).\r\n%   \r\n% Next, checkout and switch to the |feature\/cache| branch, then modify\r\n% |h_my_add.m|, say, by adding an arguments block:\r\n% \r\n%   arguments \r\n%       in1 {mustBeNumeric}\r\n%       in2 {mustBeNumeric}\r\n%   end\r\n% \r\n%   Verify locally with:\r\n% \r\n%   >> buildtool test(RunOnlyImpactedTests=1) -verbosity 3\r\n% \r\n%   *Tip:* Run the build at verbosity 3 or higher to see exactly what has\r\n%          changed and why the task is re-running.\r\n% \r\n%   *Output (excerpt)* :\r\n%   ** Starting test because:\r\n%   ** REPLACE_WITH_DASH_DASH> Input 'SourceFiles' modified\r\n%   **     REPLACE_WITH_DASH_DASH> Files modified: 'toolbox\\h_my_add.m'\r\n%   **  Evaluating task action: runTests\r\n%   Finding impacted tests...\r\n%   REPLACE_WITH_DASH_DASH> Found 8 tests impacted by changes since the last successful run (21 total).\r\n%   Setting up matlab.unittest.fixtures.ProjectFixture\r\n%   Done setting up matlab.unittest.fixtures.ProjectFixture in 0.0042568 seconds: Project 'arithmetic' is already loaded. Setup is not required.\r\n%   __________\r\n% \r\n%   Running MyAddTest\r\n%   <SNIP>\r\n%   Done MyAddTest in 0.013943 seconds\r\n%   __________\r\n% \r\n%   Running MyCalculateTest\r\n%   <SNIP>\r\n%   Done MyCalculateTest in 0.041587 seconds\r\n%   __________\r\n% \r\n%   <SNIP>    \r\n% \r\n%   Test Summary:\r\n%        Total Tests: 8\r\n%             Passed: 8\r\n%             Failed: 0\r\n%         Incomplete: 0\r\n%           Duration: 0.061731 seconds testing time.\r\n% \r\n%   <SNIP>\r\n% ** Finished test in 1.1026 seconds\r\n% \r\n% Only 8 out of 21 tests were selected to run\u2014those impacted by the\r\n% modified file. That's promising!\r\n% \r\n% Now let's push the changes and inspect the CI pipeline.\r\n% \r\n% <<2025-tia-ci-feature-push-trigger.png>>\r\n% \r\n% We see a cache hit and it confirms the |.buildtool| and |results| folder were restored. \r\n% \r\n% <<2025-tia-ci-feature-cache-hit.png>>\r\n% \r\n% The build tool leverages the restored cache to run only impacted tests.\r\n% \r\n% <<2025-tia-ci-feature-cache-run-build.png>>\r\n% \r\n%% Third Run: Modify |my_subtract.m|\r\n% Make another change, commit, and push. The cache is restored again, and\r\n% now only the tests impacted by both outstanding changes on the feature\r\n% branch are executed:\r\n% \r\n% <<2025-tia-ci-third-run-cache-hit.png>>\r\n% \r\n% <<2025-tia-ci-third-run-impacted-tests.png>>\r\n%\r\n%% Wrapping Up\r\n% There you have it! This workflow demonstrates how to combine:\r\n% * MATLAB build tool's test impact analysis\r\n% * CI platform's output caching\r\n% \r\n% ... to achieve incremental testing in CI. \r\n% \r\n% You can refine this pattern by adding things like:\r\n% * Adding cache restore keys to support fallback scenarios.\r\n% * Adjusting cache keys for multi-platform or multi-branch setups.\r\n% * Deciding when to run incremental tests vs. full regression tests.\r\n% \r\n% Would you run incremental testing in your CI pipeline? At what point in\r\n% your release cycle do you feel most comfortable running only impacted\r\n% tests?\r\n%\r\n% We anticipate investing in improving incremental testing across both\r\n% local and CI workflows. Your feedback will help us prioritize what\r\n% matters most to you.\r\n% \r\n% That's a wrap (for now) for this series on *Test Impact Analysis* but\r\n% stay tuned, we are lining up more software development topics to bring to\r\n% you!\r\n\r\n##### SOURCE END ##### 505b7ee61872420f9dcd2b3a2f0c5c47\r\n-->\r\n","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2025-tia-ci-blog-part5.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><!--introduction-->\r\n<p>In the <a href=\"https:\/\/blogs.mathworks.com\/developer\/2025\/08\/20\/tldr-too-long-didnt-run-part-3-incremental-testing-with-the-matlab-build-tool\/\">previous post<\/a> we explored how to use the MATLAB build tool to run only impacted tests during local, iterative development cycles. In this post, we'll take that concept further and apply incremental testing in a continuous integration (CI) environment to speed up feedback and improve overall throughput.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2025\/09\/29\/tldr-too-long-didnt-run-part-4-incremental-testing-in-ci\/\">read more >><\/a><\/p>","protected":false},"author":222,"featured_media":4349,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[39,4,44,57,7],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/4244"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/users\/222"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/comments?post=4244"}],"version-history":[{"count":36,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/4244\/revisions"}],"predecessor-version":[{"id":4385,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/4244\/revisions\/4385"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/4349"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=4244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=4244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=4244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}