<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Botyglot Tech Blog]]></title><description><![CDATA[The Botyglot Tech Blog]]></description><link>https://tech.botyglot.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 09:41:42 GMT</lastBuildDate><atom:link href="https://tech.botyglot.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Speed up debugging of feature tests using screen recordings]]></title><description><![CDATA[Testing is an essential part of every software development process. Feature tests, in particular, verify that our system and its features aren't throwing tantrums behind the scenes.
However, the task of debugging these feature tests is often like try...]]></description><link>https://tech.botyglot.com/speed-up-debugging-of-feature-tests-using-screen-recordings</link><guid isPermaLink="true">https://tech.botyglot.com/speed-up-debugging-of-feature-tests-using-screen-recordings</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[rubyonrails]]></category><dc:creator><![CDATA[Dorian Lupu]]></dc:creator><pubDate>Tue, 05 Mar 2024 17:02:35 GMT</pubDate><content:encoded><![CDATA[<p>Testing is an essential part of every software development process. Feature tests, in particular, verify that our system and its features aren't throwing tantrums behind the scenes.</p>
<p>However, the task of debugging these feature tests is often like trying to solve a mysterious puzzle with a clock ticking loudly in the background.</p>
<p>The complexity of feature tests, compounded by their notorious susceptibility to flakiness and with error messages that sometimes seem to be speaking alien languages, can make debugging feel less like intelligent problem-solving and more like combing a haystack for that sneaky needle.</p>
<h3 id="heading-the-superhero-of-this-article-video-captures">The superhero of this article: video captures</h3>
<p>Imagine being able to scrutinize those pesky tests in action, rewind or slow down the action to spot the villain.</p>
<p>At Botyglot, we use video captures of feature tests to fast-track our debugging process, leading to faster fixes and, let’s not forget, getting us off work in time for dinner.</p>
<h3 id="heading-off-the-shelf-solutions">Off-the-shelf solutions</h3>
<p><a target="_blank" href="https://saucelabs.com/">SauceLabs</a> and <a target="_blank" href="https://www.browserstack.com/">BrowserStack</a> offer a video recording feature that captures test executions and facilitates real-time observation and debugging. However, as the saying goes, “all good things come with a price.” The high value brought by these solutions often comes with a hefty price tag.</p>
<p>We wanted to channel our inner Pareto and find a more cost-effective alternative. So, we ventured forth on our quest.</p>
<h3 id="heading-testing-screen-recorder-gem">Testing <code>screen-recorder</code> gem</h3>
<p>Turns out we weren't the first to address this issue in the ruby community, we simply rolled up our sleeves and decided to test the <a target="_blank" href="https://github.com/kapoorlakshya/screen-recorder"><code>screen-recorder</code></a>gem in a project that was riddled with several flaky tests.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kapoorlakshya/screen-recorder">https://github.com/kapoorlakshya/screen-recorder</a></div>
<p> </p>
<p>This approach paid off dramatically.</p>
<p>We were able to comprehend and diagnose the root causes of the flaky tests relatively quickly. <strong>As a result, we successfully managed to eliminate the flakiness, thereby enhancing the reliability and robustness of our tests.</strong> This strategy not only sped up our debugging process but also improved the overall quality of our project.</p>
<h3 id="heading-adopting-the-approach-for-all-our-projects">Adopting the approach for all our projects</h3>
<p>From that moment, we incorporated this feature across all our projects, recording feature-tests on the CI, but discarding recordings of passed tests to manage costs.</p>
<p>Additionally, it boosts client trust as it assures a stronger and more reliable software system, made possible through the speedy resolution of any potential issues</p>
<h3 id="heading-show-me-the-code">Show me the code</h3>
<p>Please read the readme of the gem for installation instructions, especially the installation of FFmpeg, the underlying recording library.</p>
<p>Once that is done, here is an example on how to use the gem</p>
<pre><code class="lang-ruby"><span class="hljs-comment">#  spec/support/screen_recorder.rb</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'screen-recorder'</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'fileutils'</span>

RSpec.configuration.add_setting <span class="hljs-symbol">:screen_recorder_output_folder</span>

RSpec.configure <span class="hljs-keyword">do</span> <span class="hljs-params">|config|</span>

  <span class="hljs-keyword">unless</span> ENV[<span class="hljs-string">'DISABLE_SCREEN_RECORDER'</span>].present?

    config.screen_recorder_output_folder = File.join(Rails.root, <span class="hljs-string">'tmp'</span>, <span class="hljs-string">'screen-recorder'</span>)

    <span class="hljs-comment"># Before each feature type test, we initialize and start the screen recorder.</span>
    config.before(<span class="hljs-symbol">:each</span>, <span class="hljs-symbol">type:</span> <span class="hljs-symbol">:feature</span>) <span class="hljs-keyword">do</span>
      <span class="hljs-comment"># Make sure the folder is screen recorder output folder exists</span>
      FileUtils.mkdir_p(config.screen_recorder_output_folder) <span class="hljs-keyword">unless</span> File.directory?(config.screen_recorder_output_folder)

      <span class="hljs-comment"># Define the file path for the output video.</span>
      timestamp = Time.now.utc.strftime(<span class="hljs-string">"%Y%m%d-%H%M%S"</span>)
      @screen_recorder_output_file = File.join(config.screen_recorder_output_folder, <span class="hljs-string">"features-recording-<span class="hljs-subst">#{timestamp}</span>.mkv"</span>)

      <span class="hljs-comment"># sempahore ci screen size is 1900x1200</span>
      <span class="hljs-comment"># browser window size is also 1600x1200</span>
      advanced_config = {
        <span class="hljs-symbol">input:</span> {
          <span class="hljs-symbol">framerate:</span> <span class="hljs-number">30</span>,
          <span class="hljs-symbol">video_size:</span> <span class="hljs-string">'1600x1200'</span>
        },
        <span class="hljs-symbol">log:</span> <span class="hljs-string">'tmp/screen-recorder.log'</span>,
      }

      ScreenRecorder.logger.level = <span class="hljs-symbol">:DEBUG</span>

      <span class="hljs-comment"># Initialize the screen recorder with desired settings.</span>
      @recorder = <span class="hljs-keyword">if</span> ENV[<span class="hljs-string">'DISPLAY'</span>].present?
                    ScreenRecorder::Desktop.new(<span class="hljs-symbol">input:</span> <span class="hljs-string">"<span class="hljs-subst">#{ENV[<span class="hljs-string">'DISPLAY'</span>]}</span>"</span>, <span class="hljs-symbol">output:</span> @screen_recorder_output_file, <span class="hljs-symbol">advanced:</span> advanced_config)
                  <span class="hljs-keyword">else</span>
                    ScreenRecorder::Desktop.new(<span class="hljs-symbol">output:</span> @screen_recorder_output_file, <span class="hljs-symbol">advanced:</span> advanced_config)
                  <span class="hljs-keyword">end</span>

      @recorder.start
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment"># After each feature test, we stop the recorder and handle the output file as necessary.</span>
  config.after(<span class="hljs-symbol">:each</span>, <span class="hljs-symbol">type:</span> <span class="hljs-symbol">:feature</span>) <span class="hljs-keyword">do</span> <span class="hljs-params">|example|</span>
    <span class="hljs-keyword">unless</span> ENV[<span class="hljs-string">'DISABLE_SCREEN_RECORDER'</span>].present?

      <span class="hljs-keyword">if</span> @recorder.present?
        @recorder.stop
        <span class="hljs-keyword">if</span> example.exception <span class="hljs-params">||</span> example.metadata[<span class="hljs-symbol">:screen_record</span>]
          <span class="hljs-comment"># If the test failed, keep the recording and notify about its location.</span>
          puts <span class="hljs-string">"Recording saved to <span class="hljs-subst">#{@screen_recorder_output_file}</span>"</span>
        <span class="hljs-keyword">else</span>
          @recorder.discard
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This piece of code:</p>
<ul>
<li><p>will create a record for every feature test in the <code>tmp/screen-recorder</code> folder (except if the <code>DISABLE_SCREEN_RECORDER</code> env variable is missing)</p>
</li>
<li><p>will keep the recording only if the test failed</p>
</li>
<li><p>you can force keeping the record for a tests, even it the tests passes by adding <code>screen_record: true</code> to the test</p>
</li>
</ul>
<p>After the tests suite has been executed, simply export the <code>tmp/screen-recorder</code> folder as artifacts and you will be able to review them.</p>
<p>Keep in mind the recordings have the format <code>.mkv</code> that can be read with a software like <a target="_blank" href="https://www.videolan.org/">VLC</a>.</p>
]]></content:encoded></item></channel></rss>