<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ExecuteAutomation</title>
    <description>The latest articles on DEV Community by ExecuteAutomation (@executeautomation).</description>
    <link>https://dev.to/executeautomation</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3893154%2F2c536d16-d05e-4b26-9a27-e72cba7eb918.png</url>
      <title>DEV Community: ExecuteAutomation</title>
      <link>https://dev.to/executeautomation</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/executeautomation"/>
    <language>en</language>
    <item>
      <title>Supercharging Your CI/CD: Integrating TestSprite AI Testing with GitHub Actions</title>
      <dc:creator>ExecuteAutomation</dc:creator>
      <pubDate>Thu, 23 Apr 2026 02:48:35 +0000</pubDate>
      <link>https://dev.to/executeautomation/supercharging-your-cicd-integrating-testsprite-ai-testing-with-github-actions-3bjc</link>
      <guid>https://dev.to/executeautomation/supercharging-your-cicd-integrating-testsprite-ai-testing-with-github-actions-3bjc</guid>
      <description>&lt;p&gt;In the evolving landscape of software quality assurance, AI-driven testing is no longer a luxury—it’s a necessity for speed and scale. &lt;strong&gt;&lt;a href="https://www.testsprite.com" rel="noopener noreferrer"&gt;TestSprite&lt;/a&gt;&lt;/strong&gt;, an &lt;strong&gt;AI-powered testing platform&lt;/strong&gt;, offers a way to generate and execute end-to-end tests using agentic workflows. However, the real power of these tools is unlocked when they are integrated directly into your CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;In this article, I will demonstrate how to bridge the gap between containerized applications and cloud-based AI testing agents which is &lt;strong&gt;TestSprite&lt;/strong&gt; in our case and using it with GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Testing Private Containers from the Cloud
&lt;/h2&gt;

&lt;p&gt;When your application runs in a managed environment like Vercel or Netlify, integration is straightforward. But what if your app is running in a local Docker container or a private GitHub runner?&lt;/p&gt;

&lt;p&gt;Because the TestSprite cloud needs to "see" your application to test it, you face a networking hurdle. You cannot simply point the AI to localhost. To solve this, the video introduces a three-tier architecture: Build, Tunnel, and Execute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: The Build
&lt;/h2&gt;

&lt;p&gt;The process starts with a standard GitHub Actions runner. Using Docker Compose, the workflow spins up both the front-end (e.g., Vite) and the back-end (e.g., Node/Express) services. This ensures the environment is a perfect replica of your production stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: The Tunnel (The Secret Sauce)
&lt;/h2&gt;

&lt;p&gt;To make the private container accessible to TestSprite without exposing it permanently to the internet, the tutorial utilizes the Cloudflare Tunneling library (cloudflared).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Temporary URL: The tunnel creates a random, dynamic URL (e.g., &lt;code&gt;*.trycloudflare.com&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic Routing: This URL is passed to the TestSprite agent, serving as the &lt;code&gt;BASE_URL&lt;/code&gt; for all test execution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security: The tunnel is temporary and only exists for the duration of the test execution phase.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Phase 3: Integration and Execution
&lt;/h2&gt;

&lt;p&gt;To trigger the tests, you need to configure your GitHub repository with a few essential pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;API Key: Securely store your TestSprite API key in GitHub Repository Secrets as &lt;code&gt;TESTSPRITE_API_KEY&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workflow YAML: Define a &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; file that calls the &lt;code&gt;test-sprite/run-action@v1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once pushed, the pipeline automatically triggers on every Pull Request.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Action YAML file
&lt;/h2&gt;

&lt;p&gt;Here is how the GH Action Pipeline will look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
      &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and start app containers&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker compose up -d --build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for frontend to be ready&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Waiting for frontend on port 5173..."&lt;/span&gt;
          &lt;span class="s"&gt;timeout 60 bash -c 'until curl -s http://127.0.0.1:5173 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; do sleep 2; done'&lt;/span&gt;
          &lt;span class="s"&gt;echo "Frontend is up!"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for backend to be ready&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Waiting for backend on port 4000..."&lt;/span&gt;
          &lt;span class="s"&gt;timeout 60 bash -c 'until curl -s http://127.0.0.1:4000/employees &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; do sleep 2; done'&lt;/span&gt;
          &lt;span class="s"&gt;echo "Backend is up!"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install and start Cloudflare Tunnel&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Install cloudflared&lt;/span&gt;
          &lt;span class="s"&gt;curl -sL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb&lt;/span&gt;
          &lt;span class="s"&gt;sudo dpkg -i cloudflared.deb&lt;/span&gt;

          &lt;span class="s"&gt;# Start tunnel for frontend and capture the public URL&lt;/span&gt;
          &lt;span class="s"&gt;cloudflared tunnel --url http://127.0.0.1:5173 --no-autoupdate &amp;gt; /tmp/cloudflared.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/span&gt;
          &lt;span class="s"&gt;sleep 10&lt;/span&gt;

          &lt;span class="s"&gt;# Extract the tunnel URL from the logs&lt;/span&gt;
          &lt;span class="s"&gt;TUNNEL_URL=$(grep -o 'https://.*\.trycloudflare\.com' /tmp/cloudflared.log | head -1)&lt;/span&gt;
          &lt;span class="s"&gt;echo "Tunnel URL: $TUNNEL_URL"&lt;/span&gt;
          &lt;span class="s"&gt;echo "TUNNEL_URL=$TUNNEL_URL" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verify tunnel is working&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Testing tunnel URL: $TUNNEL_URL"&lt;/span&gt;
          &lt;span class="s"&gt;curl -sI "$TUNNEL_URL" | head -5&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rewrite hardcoded URLs in test scripts&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Replacing http://127.0.0.1:5173 with $TUNNEL_URL in test files..."&lt;/span&gt;
          &lt;span class="s"&gt;find testsprite_tests -name '*.py' -exec sed -i "s|http://127.0.0.1:5173|$TUNNEL_URL|g" {} +&lt;/span&gt;
          &lt;span class="s"&gt;echo "Replacing http://localhost:5173 with $TUNNEL_URL in test files..."&lt;/span&gt;
          &lt;span class="s"&gt;find testsprite_tests -name '*.py' -exec sed -i "s|http://localhost:5173|$TUNNEL_URL|g" {} +&lt;/span&gt;
          &lt;span class="s"&gt;# Show a sample to verify&lt;/span&gt;
          &lt;span class="s"&gt;grep -r 'trycloudflare' testsprite_tests/*.py | head -3 || echo "No replacements found - check URL pattern"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Testsprite Action&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TestSprite/run-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;testsprite-api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TESTSPRITE_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;base_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.TUNNEL_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;blocking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Results: AI-Powered PR Reports
&lt;/h2&gt;

&lt;p&gt;The most impressive part of this integration is the feedback loop. Once the tests are complete:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;PR Blocking: If tests fail, the Pull Request is automatically blocked from merging, ensuring code quality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comprehensive Dashboards: TestSprite provides a detailed dashboard within the PR, showing exactly which cases passed or failed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Video Evidence: Perhaps most helpfully, the platform provides video recordings of the AI agent interacting with the browser, making it incredibly easy to debug failures.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most impressive part of this integration is the feedback loop. Once the tests are complete:&lt;/p&gt;

&lt;p&gt;PR Blocking: If tests fail, the Pull Request is automatically blocked from merging, ensuring code quality.&lt;/p&gt;

&lt;p&gt;Comprehensive Dashboards: TestSprite provides a detailed dashboard within the PR, showing exactly which cases passed or failed.&lt;/p&gt;

&lt;p&gt;Video Evidence: Perhaps most helpfully, the platform provides video recordings of the AI agent interacting with the browser, making it incredibly easy to debug failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Integrating AI testing into GitHub Actions transforms QA from a bottleneck into a seamless part of the development cycle. By combining Docker, Cloudflare tunneling, and TestSprite, teams can achieve high-confidence deployments with minimal manual intervention.&lt;/p&gt;

&lt;p&gt;For a detailed walkthrough of the code and configuration, check out the full video by Execute Automation: &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/8ZuOrdcBgJ4"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testsprite</category>
      <category>executeautomation</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
