<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Testing on Damian Galarza | Software Engineering &amp; AI Consulting</title><link>https://www.damiangalarza.com/tags/testing/</link><description>Recent posts from Damian Galarza | Software Engineering &amp; AI Consulting</description><generator>Hugo</generator><language>en-us</language><managingEditor>Damian Galarza</managingEditor><atom:link href="https://www.damiangalarza.com/tags/testing/feed.xml" rel="self" type="application/rss+xml"/><item><title>AI Agent Evals: The 4 Layers Most Teams Skip</title><link>https://www.damiangalarza.com/videos/2026-04-07-ai-agent-evals-the-4-layers-most-teams-skip/</link><pubDate>Tue, 07 Apr 2026 14:00:34 +0000</pubDate><author>Damian Galarza</author><guid>https://www.damiangalarza.com/videos/2026-04-07-ai-agent-evals-the-4-layers-most-teams-skip/</guid><description>Most teams evaluate AI agents by vibes. Here are the four layers of evals you actually need to ship agents with confidence.</description><content:encoded><![CDATA[<p>Most teams evaluate AI agents by vibes. Here are the four layers of evals you actually need to ship agents with confidence.</p>
<p>I walk through the eval stack I use on real agent projects — from unit-level prompt checks up through end-to-end trajectory scoring — and explain where each layer catches different classes of failure. If you&rsquo;re building agents for production and wondering why regressions keep slipping through, this is the framework to borrow.</p>
]]></content:encoded></item><item><title>Acceptance Tests with Subdomains</title><link>https://www.damiangalarza.com/posts/2016-10-10-acceptance-tests-with-subdomains/</link><pubDate>Mon, 10 Oct 2016 00:00:00 -0400</pubDate><author>Damian Galarza</author><guid>https://www.damiangalarza.com/posts/2016-10-10-acceptance-tests-with-subdomains/</guid><description>How to use subdomains in your feature specs.</description><content:encoded><![CDATA[<p>This post was originally published on the <a href="https://thoughtbot.com/blog/acceptance-tests-with-subdomains">thoughtbot blog</a>.</p>
<p>You&rsquo;re excited about building a new application which allows users to sign up and host their own blog. You decide that each blog will have their own space by providing a subdomain.</p>
<p>Let&rsquo;s start off with a feature spec.</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#89dceb">require</span> <span style="color:#a6e3a1">&#34;rails_helper&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>feature <span style="color:#a6e3a1">&#34;user views a blog&#34;</span> <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>  scenario <span style="color:#a6e3a1">&#34;homepage&#34;</span> <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>     blog <span style="color:#89dceb;font-weight:bold">=</span> create(
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">:blog</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">subdomain</span>: <span style="color:#a6e3a1">&#34;bobloblaw&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">title</span>: <span style="color:#a6e3a1">&#34;Bob Loblaw&#39;s Law Blog&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">description</span>: <span style="color:#a6e3a1">&#34;Welcome to my new blog.&#34;</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    visit root_path
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    expect(page)<span style="color:#89dceb;font-weight:bold">.</span>to have_content blog<span style="color:#89dceb;font-weight:bold">.</span>title
</span></span><span style="display:flex;"><span>    expect(page)<span style="color:#89dceb;font-weight:bold">.</span>to have_content blog<span style="color:#89dceb;font-weight:bold">.</span>description
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><p>In our app we render the blog homepage using the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#6c7086;font-style:italic"># config/routes.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#f9e2af">Rails</span><span style="color:#89dceb;font-weight:bold">.</span>application<span style="color:#89dceb;font-weight:bold">.</span>routes<span style="color:#89dceb;font-weight:bold">.</span>draw <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>  root <span style="color:#a6e3a1">to</span>: <span style="color:#a6e3a1">&#34;blogs#show&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#6c7086;font-style:italic"># app/controllers/blogs_controller.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">class</span> <span style="color:#f9e2af">BlogsController</span> <span style="color:#89dceb;font-weight:bold">&lt;</span> <span style="color:#f9e2af">ApplicationController</span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">def</span> <span style="color:#89b4fa">show</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f5e0dc">@blog</span> <span style="color:#89dceb;font-weight:bold">=</span> current_blog
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">private</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">def</span> <span style="color:#89b4fa">current_blog</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f5e0dc">@_current_blog</span> <span style="color:#89dceb;font-weight:bold">||=</span> <span style="color:#f9e2af">Blog</span><span style="color:#89dceb;font-weight:bold">.</span>find_by(<span style="color:#a6e3a1">subdomain</span>: request<span style="color:#89dceb;font-weight:bold">.</span>subdomains<span style="color:#89dceb;font-weight:bold">.</span>first)
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#6c7086;font-style:italic">&lt;!-- app/views/blogs/show.html.erb --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#cba6f7">h1</span>&gt;<span style="color:#f38ba8">&lt;</span>%= @blog.title %&gt;&lt;/<span style="color:#cba6f7">h1</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#cba6f7">p</span>&gt;<span style="color:#f38ba8">&lt;</span>%= @blog.description %&gt;&lt;/<span style="color:#cba6f7">p</span>&gt;
</span></span></code></pre></div><p>In order to visit the homepage via a subdomain in our test we need to set the <code>app_host</code> property for Capybara. We could try to use <code>myblog.localhost</code> but Rails will think that localhost is the top level domain and therefore won&rsquo;t see myblog as a subdomain. Instead we&rsquo;ll use a fake host name <code>example.com</code>. We can set it by adding the following to our spec before calling <code>visit</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#a6e3a1">&#34;http://myblog.example.com&#34;</span>
</span></span></code></pre></div><p>If we run the test with the default Capybara driver, <code>rack-test</code> it should be green.  <code>rack-test</code> interacts directly with Rack which means it never uses the external URL. If we need to use a JavaScript driver however we will need to use an actual accessible URL. Add the <code>:js</code> metadata to the scenario and you should see a failure occur.</p>
<p>In order to accommodate a driver like Selenium or capybara-webkit we&rsquo;ll need to do some more work. To start, we will not be able to use our fake host <code>example.com</code>. Instead we need a host name which will point to <code>127.0.0.1</code>. There is one readily available to us for use through <code>lvh.me</code>. Its DNS records are set up so that <code>lvh.me</code> and all of its subdomains resolve to your local machine at <code>127.0.0.1</code>.</p>
<p>So update <code>app_host</code> from <code>http://myblog.example.com</code> to <code>http://myblog.lvh.me</code>. We&rsquo;re still not done yet though.</p>
<p>Next, we need to instruct Capybara to include the port number for the Capybara server in all requests to work correctly. We can do that by adding the following to <code>spec/rails_helper.rb</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>configure <span style="color:#cba6f7">do</span> <span style="color:#89dceb;font-weight:bold">|</span>config<span style="color:#89dceb;font-weight:bold">|</span>
</span></span><span style="display:flex;"><span>  config<span style="color:#89dceb;font-weight:bold">.</span>always_include_port <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#cba6f7">true</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><p>If you&rsquo;re using the capybara-webkit driver and configuring it to block all unknown URLs <a href="https://github.com/thoughtbot/suspenders/blob/master/templates/capybara_webkit.rb#L4">as we do in Suspenders</a> then you&rsquo;ll need to do one more thing. In the configuration for capybara-webkit you&rsquo;ll need to add the <code>lvh.me</code> host to the URL whitelist. If you&rsquo;re using a Suspenders based app then open up <code>spec/support/capybara_webkit.rb</code> or whichever file you have configured capybara-webkit in. Update the configuration to look like:</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">::</span><span style="color:#f9e2af">Webkit</span><span style="color:#89dceb;font-weight:bold">.</span>configure <span style="color:#cba6f7">do</span> <span style="color:#89dceb;font-weight:bold">|</span>config<span style="color:#89dceb;font-weight:bold">|</span>
</span></span><span style="display:flex;"><span>  config<span style="color:#89dceb;font-weight:bold">.</span>block_unknown_urls
</span></span><span style="display:flex;"><span>  config<span style="color:#89dceb;font-weight:bold">.</span>allow_url(<span style="color:#a6e3a1">&#34;myblog.lvh.me&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><p>This will allow Capybara to access our blog through <code>lvh.me</code> and not block it. With this in place we can run our tests and things should be green again.</p>
<h2 id="allowing-more-subdomains">Allowing more subdomains</h2>
<p>Things are working great with the above but we realize that we are coupled to the <code>myblog</code> subdomain within all of our tests. We will finish things off by making this more flexible.</p>
<p>Let&rsquo;s start by updating our capybara-webkit configuration to allow all subdomains on lvh.me and not just limiting it to <code>myblog</code>. We can do this by changing <code>myblog</code> to <code>*</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">::</span><span style="color:#f9e2af">Webkit</span><span style="color:#89dceb;font-weight:bold">.</span>configure <span style="color:#cba6f7">do</span> <span style="color:#89dceb;font-weight:bold">|</span>config<span style="color:#89dceb;font-weight:bold">|</span><span style="color:#f38ba8">¬</span>
</span></span><span style="display:flex;"><span>  config<span style="color:#89dceb;font-weight:bold">.</span>block_unknown_urls<span style="color:#f38ba8">¬</span>
</span></span><span style="display:flex;"><span>  config<span style="color:#89dceb;font-weight:bold">.</span>allow_url(<span style="color:#a6e3a1">&#34;*.lvh.me&#34;</span>)<span style="color:#f38ba8">¬</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><p>Next, let&rsquo;s extract a helper method to make testing subdomains easier.</p>
<p>We&rsquo;ll add the following method to our feature spec:</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#cba6f7">def</span> <span style="color:#89b4fa">visit_blog</span>(blog, path <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#a6e3a1">&#39;/&#39;</span>)
</span></span><span style="display:flex;"><span>  app_host <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#f9e2af">URI</span><span style="color:#89dceb;font-weight:bold">.</span>join(<span style="color:#a6e3a1">&#34;http://</span><span style="color:#a6e3a1">#{</span>blog<span style="color:#89dceb;font-weight:bold">.</span>subdomain<span style="color:#a6e3a1">}</span><span style="color:#a6e3a1">.lvh.me&#34;</span>, path)<span style="color:#89dceb;font-weight:bold">.</span>to_s
</span></span><span style="display:flex;"><span>  using_app_host(app_host) <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>    visit path
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">def</span> <span style="color:#89b4fa">using_app_host</span>(host)
</span></span><span style="display:flex;"><span>  original_host <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host
</span></span><span style="display:flex;"><span>  <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host <span style="color:#89dceb;font-weight:bold">=</span> host
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">yield</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">ensure</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host <span style="color:#89dceb;font-weight:bold">=</span> original_host
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div><p><code>using_app_host</code> allows us to pass a host for Capybara to use and temporarily overrides the <code>app_host</code> rather then permanently setting it. Our use of <code>ensure</code> makes sure that the <code>app_host</code> is always set back to its original value regardless of exceptions being raised while <code>yield</code>ing the block. <code>visit_blog</code> allows us to pass an instance of a blog as well as a path to visit. By default, this path is the root of the blog.</p>
<p>So we can update our spec to look as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#89dceb">require</span> <span style="color:#a6e3a1">&#34;rails_helper&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>feature <span style="color:#a6e3a1">&#34;user views a blog&#34;</span> <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>  scenario <span style="color:#a6e3a1">&#34;homepage&#34;</span>, <span style="color:#a6e3a1">:js</span> <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>     blog <span style="color:#89dceb;font-weight:bold">=</span> create(
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">:blog</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">subdomain</span>: <span style="color:#a6e3a1">&#34;bobloblaw&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">title</span>: <span style="color:#a6e3a1">&#34;Bob Loblaw&#39;s Law Blog&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e3a1">description</span>: <span style="color:#a6e3a1">&#34;Welcome to my new blog.&#34;</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    visit_blog blog
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    expect(page)<span style="color:#89dceb;font-weight:bold">.</span>to have_content blog<span style="color:#89dceb;font-weight:bold">.</span>title
</span></span><span style="display:flex;"><span>    expect(page)<span style="color:#89dceb;font-weight:bold">.</span>to have_content blog<span style="color:#89dceb;font-weight:bold">.</span>description
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">def</span> <span style="color:#89b4fa">visit_blog</span>(blog, path <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#a6e3a1">&#39;/&#39;</span>)
</span></span><span style="display:flex;"><span>    app_host <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#f9e2af">URI</span><span style="color:#89dceb;font-weight:bold">.</span>join(<span style="color:#a6e3a1">&#34;http://</span><span style="color:#a6e3a1">#{</span>blog<span style="color:#89dceb;font-weight:bold">.</span>subdomain<span style="color:#a6e3a1">}</span><span style="color:#a6e3a1">.lvh.me&#34;</span>, path)<span style="color:#89dceb;font-weight:bold">.</span>to_s
</span></span><span style="display:flex;"><span>    using_app_host(app_host) <span style="color:#cba6f7">do</span>
</span></span><span style="display:flex;"><span>      visit path
</span></span><span style="display:flex;"><span>    <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">def</span> <span style="color:#89b4fa">using_app_host</span>(host)
</span></span><span style="display:flex;"><span>    original_host <span style="color:#89dceb;font-weight:bold">=</span> <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host
</span></span><span style="display:flex;"><span>    <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host <span style="color:#89dceb;font-weight:bold">=</span> host
</span></span><span style="display:flex;"><span>    <span style="color:#cba6f7">yield</span>
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">ensure</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f9e2af">Capybara</span><span style="color:#89dceb;font-weight:bold">.</span>app_host <span style="color:#89dceb;font-weight:bold">=</span> original_host
</span></span><span style="display:flex;"><span>  <span style="color:#cba6f7">end</span>
</span></span><span style="display:flex;"><span><span style="color:#cba6f7">end</span>
</span></span></code></pre></div>]]></content:encoded></item></channel></rss>