<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://gergo.dev/atom.xml" rel="self" type="application/atom+xml" /><link href="https://gergo.dev/" rel="alternate" type="text/html" /><updated>2025-03-02T20:53:06+00:00</updated><id>https://gergo.dev/atom.xml</id><title type="html">Stage whispers</title><subtitle>Intended to be overheard.</subtitle><author><name>Gergő Sulymosi</name><email>gergo.sulymosi@gmail.com</email></author><entry><title type="html">Arrange, act and assert with RSpec</title><link href="https://gergo.dev/arrange-act-assert-with-rspec" rel="alternate" type="text/html" title="Arrange, act and assert with RSpec" /><published>2025-01-04T14:34:01+00:00</published><updated>2025-01-04T14:34:01+00:00</updated><id>https://gergo.dev/arrange-act-assert-with-rspec</id><content type="html" xml:base="https://gergo.dev/arrange-act-assert-with-rspec"><![CDATA[<p>Are your tests helping or hindering your development process? In this article, I explore the Arrange-Act-Assert pattern, a simple yet effective way to structure your tests for clarity and sustainability. Whether you’re a seasoned RSpec user or just starting out, you’ll learn how to turn natural-language statements into expressive, human-readable tests that stand the test of time.</p>

<p>Automated tests replace tedious manual testing whenever you change your code with nearly instant feedback<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Automated tests can also serve as a living documentation of your system and its behavior.</p>

<p>To earn these benefits, <strong>you need to write the tests with their reader in mind</strong>. The reader – quite often you – must understand the test’s <em>prerequisites</em>, <em>purpose</em>, and <em>expected outcomes</em>. In other words, we have to write brief, explicit, and predictable tests, so maintaining them is sustainable in the long run.</p>

<p><a href="https://rspec.info">RSpec</a> is a testing framework for Ruby, providing a <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL</a> to write expressive, human-readable automated tests. Tests written using RSpec read like common English and use a rich set of syntax to describe the system’s behavior.</p>

<h2 id="the-pattern">The pattern</h2>

<p>In the last decade, I found one pattern that made my tests notably better for their readers. In a 2011 article, <a href="https://xp123.com/articles/3a-arrange-act-assert/">Bill Wake</a> called this pattern <em>arrange-act-assert</em> (AAA or 3A). You might recognize a striking similarity between <em>arrange-act-assert</em> and the given-when-then pattern from <a href="https://leanpub.com/bddbooks-formulation">behavior-driven development</a>.</p>

<p>We are great at <a href="https://en.wikipedia.org/wiki/Pattern_recognition_(psychology)">recognizing structural patterns</a>. If the structure of our tests consistently follows a pattern, the readers will understand them quicker and, when needed, change and maintain them <em>confidently</em>.</p>

<ol>
  <li>The <em>“arrange”</em> step sets up the test case, instantiates any necessary collaborating objects, configures settings, and prepares the database, …</li>
  <li>The <em>“act”</em> step covers the main <em>action</em>. Usually, this means a method call in a unit test, an HTTP request in integration tests, and browser interactions within system tests.</li>
  <li>The <em>“assert”</em> step compares the results with the expectations. Is the return value equal to the expected? Did the expected side-effect happen?</li>
</ol>

<p>You won’t find any of these terms (arrange, act, assert) in RSpec’s DSL, but that only means that RSpec has a different terminology.</p>

<h2 id="rspecs-grammar">RSpec’s grammar</h2>

<p>Let me show you how I think about and structure my tests using RSpec. I will do that by writing and refactoring some tests for a simple object. The simple object will be a socially adept robot who is aware of its robot nature.</p>

<p>When I write tests for an application, I formalize the tests as factual statements about the system I’m describing on a high level:</p>

<blockquote>
  <p>A social robot greets and introduces itself to the user.<br />
A social robot confirms that it is not a human.</p>
</blockquote>

<p>When I’m writing unit tests, I switch my subjects and verbs to class names and method names:</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">SocialRobot#introduce</code> prints a greeting and introduction to the STDOUT.<br />
<code class="language-plaintext highlighter-rouge">SocialRobot#human?</code> returns false.</p>
</blockquote>

<p>These statements translate well into RSpec’s grammar. To scaffold a test, you need to dissect the sentences into their subjects, verbs, and the rest.</p>

<p>The subject of the sentence becomes the subject of the test, and RSpec indicates it using <code class="language-plaintext highlighter-rouge">#describe</code>. The verb, in high-level tests (system, integration, …) becomes part of the behavior with the rest of the sentence, in unit tests becomes part of the subject usually as a nested <code class="language-plaintext highlighter-rouge">#describe</code> block.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># High-level tests like system and integration tests</span>
<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="s2">"A social robot"</span> <span class="k">do</span>
  <span class="n">it</span> <span class="s2">"greets and introduces itself to the user"</span>
<span class="k">end</span>

<span class="c1"># Lower-level unit tests</span>
<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"prints a greeting and introduction to the STDOUT"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>RSpec has sophisticated grammar, and we just started with the scaffold of a test. I’m going to continue with the unit test variant to cover the majority of the <a href="https://martinfowler.com/articles/practical-test-pyramid.html">testing pyramid</a>.</p>
<h2 id="lets-arrange">Let’s Arrange</h2>

<p>RSpec has several helpers to express and define the necessary steps to prepare for a test.
The <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/Hooks.html#before-instance_method"><code class="language-plaintext highlighter-rouge">#before</code></a> method is RSpec’s trivial way of saying <em>arrange</em>. Within its block, you can set up everything that is required by the test.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">before</span> <span class="k">do</span>
      <span class="no">I18n</span><span class="p">.</span><span class="nf">locale</span> <span class="o">=</span> <span class="ss">:en</span>
      <span class="vi">@name</span> <span class="o">=</span> <span class="s2">"Bob"</span>
      <span class="vi">@test_subject</span> <span class="o">=</span> <span class="no">SocialRobot</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@name</span><span class="p">)</span>
    <span class="k">end</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">SocialRobot</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Bob"</span><span class="p">)</span>
    <span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>What about tear down? You can implement <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/Hooks.html#after-instance_method"><code class="language-plaintext highlighter-rouge">#after</code></a> and <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/Hooks.html#around-instance_method"><code class="language-plaintext highlighter-rouge">#around</code></a> hooks for your tests. Like <code class="language-plaintext highlighter-rouge">#before</code>, these methods accept arguments to specify when to execute them. They allow us to clean up the application state, roll back database changes, and delete files created by the tests…</p>

<h3 id="breaking-up-arrange">Breaking up “arrange”</h3>

<p>RSpec gets more sophisticated than that. It has words to define the object you’re testing, your test <em>subject</em>, and its <em>collaborators</em>. So when you <em>arrange</em> your test, you can give them names.</p>

<h4 id="subject---what-are-we-testing">Subject - what are we testing?</h4>

<p>I’ll start with the <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods.html#subject-instance_method"><code class="language-plaintext highlighter-rouge">#subject(name = nil) {}</code></a> method. I use <code class="language-plaintext highlighter-rouge">#subject</code> to tell future me, my colleagues and RSpec about the object under testing. Specifying the <em>subject</em> this way will also allow us to reference it implicitly. This is why you can write, for example, <code class="language-plaintext highlighter-rouge">it { is_expected.to eq("foo") }</code>.</p>

<p>I prefer to name these subjects, so when I’m writing more complex expectations, I can use descriptive names. Naming things is especially handy when the subject differs between test cases.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">subject</span><span class="p">(</span><span class="ss">:robot</span><span class="p">)</span> <span class="p">{</span> <span class="no">SocialRobot</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@name</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># 👈 Extracted from #before</span>

    <span class="n">before</span> <span class="k">do</span>
      <span class="no">I18n</span><span class="p">.</span><span class="nf">locale</span> <span class="o">=</span> <span class="ss">:en</span>
      <span class="vi">@name</span> <span class="o">=</span> <span class="s2">"Bob"</span>
    <span class="k">end</span>

    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h4 id="let---define-the-collaborators">Let - define the collaborators</h4>

<p>Let’s follow it up with <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods.html#let-instance_method"><code class="language-plaintext highlighter-rouge">#let(name, &amp;block)</code></a>. Using <code class="language-plaintext highlighter-rouge">#let</code>, you can extract, name (and memoize) the <em>collaborators</em> of your test subject. The test collaborators are the objects that your test subject needs to exist and function. Often these objects are substituted with <a href="https://rspec.info/features/3-13/rspec-mocks/">mocks, doubles, and spies</a>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">subject</span><span class="p">(</span><span class="ss">:robot</span><span class="p">)</span> <span class="p">{</span> <span class="no">SocialRobot</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># 👈 Uses the memoized method instead of the ivar</span>
    <span class="n">let</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span> <span class="p">{</span> <span class="s2">"Bob"</span> <span class="p">}</span>                      <span class="c1"># 👈 Extracted from #before</span>

    <span class="n">before</span> <span class="p">{</span> <span class="no">I18n</span><span class="p">.</span><span class="nf">locale</span> <span class="o">=</span> <span class="ss">:en</span> <span class="p">}</span>

    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="a-word-of-warning-about-let">A word of warning about <em>let</em></h3>

<p>Use <code class="language-plaintext highlighter-rouge">#let</code> <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods.html#let-instance_method">sparingly</a>! A couple can improve readability, but more will quickly deteriorate your test. If I find a test surrounded by a bunch of <em>let</em> statements, usually there is a whiff of the <a href="https://refactoring.guru/smells/long-parameter-list">Long Parameter List</a> code smell too.</p>

<p>Another issue I often see is using <em>let</em> to store expectations. This will force the reader to jump back and forth in the test code. Jumping around makes us dizzy and confused. Keep the expectations together with the assertions where they belong.</p>

<p><a href="https://x.com/joeferris">Joe Ferries</a> wrote an excellent <a href="https://thoughtbot.com/blog/lets-not">article</a> about potential overuse issues. I think “the dose makes the poison,” so be mindful of using <em>let</em>.</p>

<h2 id="act-and-assert">Act and Assert</h2>

<p>After arranging everything for the test, it’s time to implement the rest of those factual sentences. This is the meat of every test, where you implement the verb by calling the tested method and setting expectations. In RSpec, both <em>act</em> and <em>arrange</em> steps are enclosed within <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/ExampleGroup.html#it-class_method"><code class="language-plaintext highlighter-rouge">#it</code></a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> blocks. There are distinct ways to implement assertions and expectations using  RSpec’s DSL.</p>

<p>I find it idiomatic for RSpec to use the principle of <a href="https://en.wikipedia.org/wiki/Command%E2%80%93query_separation">command-query separation</a> to choose how I set assertions. Using a consistent assertion style makes my tests predictable and keeps me writing <a href="https://en.wikipedia.org/wiki/Command%E2%80%93query_separation#Broader_impact_on_software_engineering">cleaner code</a>.</p>

<blockquote>
  <p>Command-Query Separation (or CQS for short) states that every method should either be a command that performs an action and creates side effects, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer.</p>
</blockquote>

<p>The <em>general form of setting expectations</em> is <code class="language-plaintext highlighter-rouge">expect(&lt;value&gt;).to &lt;matcher&gt;</code>, or you can also negate the expectation using <code class="language-plaintext highlighter-rouge">.not_to</code>, turning the expression into <code class="language-plaintext highlighter-rouge">expect(&lt;value&gt;).not_to &lt;matcher&gt;</code>.</p>

<p>RSpec calls its assertion helpers <a href="https://rspec.info/documentation/3.12/rspec-expectations/RSpec/Matchers.html"><em>matchers</em></a> and <a href="https://rspec.info/documentation/3.12/rspec-mocks/RSpec/Mocks.html"><em>mocks</em></a>. RSpec encourages us to write common English-sounding expectations, aided by its rich set of methods to set expectations and verify message passing between objects.
Knowing these methods and using the most adequate to express the more complex expectations is so rewarding<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. You can even rely on them to document your test cases. Furthermore, you can <a href="https://rspec.info/features/3-12/rspec-expectations/custom-matchers/define-matcher/">write your custom matchers</a> to extend RSpec’s DSL.</p>

<h3 id="assert-return-values">Assert return values</h3>

<p>Based on CQS, return values come from query methods. If you use Ruby on Rails, these query methods are not exclusive to ActiveRecord queries but mean any method that returns a value, most preferably without side-effects.</p>

<p>We can turn the “<code class="language-plaintext highlighter-rouge">SocialRobot#human?</code> returns false.” sentence into the following test:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#human?"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"returns false"</span> <span class="k">do</span>
      <span class="n">expect</span><span class="p">(</span><span class="n">robot</span><span class="p">.</span><span class="nf">human?</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="kp">false</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">SocialRobot</span>
  <span class="k">def</span> <span class="nf">human?</span> <span class="o">=</span> <span class="kp">false</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Ruby is a brief but comprehensive language, and RSpec inherits these characteristics. In my humble opinion, the above test is succinct and English-like. It is explicit with the method call and the expected value next to each other. I like to keep most of my tests in this form. It’s easy to understand and maintain and requires minimal effort from developers coming from a different languages or framework.</p>

<p>For teams with seasoned Ruby developers RSpec provides briefer ways to express the same expectation using some meta-programming and nifty helpers.
The previous example can be boiled down to a single-line test carrying the same meaning and providing a similar documentation value.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="p">.</span><span class="nf">not_to</span> <span class="n">be_human</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p>So how does this work? Because the <code class="language-plaintext highlighter-rouge">#human?</code> method (in this example) will always return <code class="language-plaintext highlighter-rouge">false</code> we can put the test directly within the main describe block. You can also omit the description of the <code class="language-plaintext highlighter-rouge">it</code> block because it will be inferred from the expectation’s matcher. You can use the <a href="https://rspec.info/documentation/3.12/rspec-core/RSpec/Core/MemoizedHelpers.html#is_expected-instance_method"><code class="language-plaintext highlighter-rouge">#is_expected</code></a> method, a shorthand for the <code class="language-plaintext highlighter-rouge">expect(subject)</code> expression. Finally, we can use the <a href="https://www.rubydoc.info/gems/rspec-expectations/RSpec%2FMatchers:be"><code class="language-plaintext highlighter-rouge">be_*</code></a> matcher to invoke the matching <a href="http://ruby-for-beginners.rubymonstas.org/objects/predicates.html">predicate method</a> on the test subject and assert it returns <code class="language-plaintext highlighter-rouge">true</code>. In the case of <code class="language-plaintext highlighter-rouge">be_human</code> the matcher will call the <code class="language-plaintext highlighter-rouge">#human?</code> method on the <code class="language-plaintext highlighter-rouge">subject</code>, which is an instance of the <code class="language-plaintext highlighter-rouge">SocialRobot</code> class.</p>

<h3 id="assert-side-effects-and-behavior">Assert side effects and behavior</h3>

<p>Writing clear and concise expectations for side effects is fiddly because setting the expectation must be split into two parts. Executing the command (calling the function/method) and setting expectations on the side effects. Setting expectations on the side effects can happen in multiple formats.</p>

<h4 id="using-a-query-method">Using a query method</h4>

<p>The simplest case is when a query method is used to check the side effects of a command method. This looks exactly like the above, with an extra line to call the command method.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#name="</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"sets the name"</span> <span class="k">do</span>
      <span class="n">robot</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="s2">"Bob"</span>
      <span class="n">expect</span><span class="p">(</span><span class="n">robot</span><span class="p">.</span><span class="nf">name</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="s2">"Bob"</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">SocialRobot</span>
  <span class="nb">attr_accessor</span> <span class="ss">:name</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In this situation, from time to time, I find myself setting two expectations. One before I call the command-method and another afterwards. This might sound paranoid, but so many times had I seen cases, where the query method returned the expected value even before the call to the command method. It is really important that you have a high level of trust in your tests.</p>

<h4 id="expecting-a-change">Expecting a change</h4>

<p>RSpec’s creators already considered the previous issue and created the <code class="language-plaintext highlighter-rouge">expect { ... }.to change { ... }</code> form. The <code class="language-plaintext highlighter-rouge">change { }</code> matcher uses a query method to test side effects and ensure a change in the return value.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#name="</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"sets the name"</span> <span class="k">do</span>
      <span class="n">expect</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="s2">"Chris"</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">to</span> <span class="n">change</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">name</span> <span class="p">}.</span><span class="nf">from</span><span class="p">(</span><span class="s2">"Bob"</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="s2">"Chris"</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This is usually the form I use to test methods with side effects. It gives me the confidence that if the test arrangement/setup interferes with the expectation, the test will fail. Keep in mind that mixing Ruby’s block syntax and method chaining can be hard to read and confusing even for seasoned Ruby programmers.</p>

<h4 id="using-mocks">Using mocks</h4>

<p>Mocks are useful for setting expectations about the collaboration between objects. Mocks verify if an object has received the messages. Mocks are handy when the side effect should be avoided due to resource requirements (calculating large primes or calling a long chain of methods) or the irreversible consequences (payment).</p>

<p>RSpec has a feature-rich <a href="https://rspec.info/documentation/3.13/rspec-mocks/">mocking library</a> with mocks, stubs, and spies. Introducing the library could fill a book, so I’m keeping it to a single example.</p>

<p>Note: Using mocks to set expectations, in other words, to assert, swaps up the order of arrange-act-assert steps. RSpec has spies <code class="language-plaintext highlighter-rouge">expect(...).to have_received()</code> to keep the order, but I personally favor setting message-passing expectations upfront.</p>

<p>The last feature of the social robot is introducing itself through the standard out stream.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"prints a greeting and introduction to the STDOUT."</span> <span class="k">do</span>
      <span class="n">expect</span><span class="p">(</span><span class="no">STDOUT</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:puts</span><span class="p">).</span><span class="nf">with</span><span class="p">(</span><span class="s2">"Hi! I'm Bob"</span><span class="p">)</span>

      <span class="n">robot</span><span class="p">.</span><span class="nf">introduce</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">SocialRobot</span>
  <span class="k">def</span> <span class="nf">introduce</span>
    <span class="nb">puts</span> <span class="s2">"Hi! I'm </span><span class="si">#{</span><span class="vi">@name</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>RSpec offers a rich set of expressive matchers and has something for this test as well. Because expecting output on streams is a common test assertion, RSpec has a <a href="https://www.rubydoc.info/gems/rspec-expectations/RSpec%2FMatchers:output">specific matcher</a> for this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"prints a greeting and introduction to the STDOUT."</span> <span class="k">do</span>
      <span class="n">expect</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">introduce</span> <span class="p">}.</span><span class="nf">to</span> <span class="n">output</span><span class="p">(</span><span class="s2">"Hi! I'm Bob</span><span class="se">\n</span><span class="s2">"</span><span class="p">).</span><span class="nf">to_stdout</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>
<p>My intention behind this article was to capture why arrange-act-assert is a great structural pattern for organizing automated tests. It provides a recognizable and predictable rhythm that aids the readers.</p>

<p>Another useful pattern for further refining tests is command-query-separation. CQS provides a single litmus test for deciding how to test specific methods, and it guides me in writing cleaner code.</p>

<p>Using factual statements is highly effective when considering the system or object under testing because they naturally match RSpec’s grammar.</p>

<p>Finally, RSpec, just like Ruby, has several sophisticated ways to describe your intentions. Many of its nifty tools are prone to abuse or misuse. Keep your reader in mind when you use them.</p>

<h2 id="final-solution">Final solution</h2>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'rspec/autorun'</span>

<span class="k">class</span> <span class="nc">SocialRobot</span>
  <span class="nb">attr_accessor</span> <span class="ss">:name</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Bob"</span><span class="p">)</span>
    <span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">human?</span> <span class="o">=</span> <span class="kp">false</span>

  <span class="k">def</span> <span class="nf">introduce</span>
    <span class="nb">puts</span> <span class="s2">"Hi! I'm </span><span class="si">#{</span><span class="vi">@name</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">SocialRobot</span> <span class="k">do</span>
  <span class="n">subject</span><span class="p">(</span><span class="ss">:robot</span><span class="p">)</span> <span class="p">{</span> <span class="n">described_class</span><span class="p">.</span><span class="nf">new</span> <span class="p">}</span>

  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="p">.</span><span class="nf">not_to</span> <span class="n">be_human</span> <span class="p">}</span>

  <span class="n">describe</span> <span class="s2">"#name="</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"sets the name"</span> <span class="k">do</span>
      <span class="n">expect</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="s2">"Chris"</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">to</span><span class="p">(</span><span class="n">change</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">name</span> <span class="p">}.</span><span class="nf">from</span><span class="p">(</span><span class="s2">"Bob"</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="s2">"Chris"</span><span class="p">))</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="n">describe</span> <span class="s2">"#introduce"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="s2">"prints a greeting and introduction to the STDOUT."</span> <span class="k">do</span>
      <span class="n">expect</span> <span class="p">{</span> <span class="n">robot</span><span class="p">.</span><span class="nf">introduce</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">to</span> <span class="n">output</span><span class="p">(</span><span class="s2">"Hi! I'm Bob</span><span class="se">\n</span><span class="s2">"</span><span class="p">).</span><span class="nf">to_stdout</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>However, one must see the test fail for the right reason. I beg you never to skip seeing the test fail for the right reasons. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Alternatively into a <code class="language-plaintext highlighter-rouge">#specify</code> block. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Please consider your team’s knowledge of these matchers and mocks. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Gergő Sulymosi</name><email>gergo.sulymosi@gmail.com</email></author><category term="Testing" /><summary type="html"><![CDATA[Are your tests helping or hindering your development process? In this article, I explore the power of the Arrange-Act-Assert pattern, a simple yet effective way to structure your tests for clarity and sustainability. Whether you're a seasoned RSpec user or just starting out, you'll learn how to turn natural-language statements into expressive, human-readable tests that stand the test of time.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gergo.dev/images/jap-mountains.jpeg" /><media:content medium="image" url="https://gergo.dev/images/jap-mountains.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">On concise test setups with FactoryBot</title><link href="https://gergo.dev/factory-bot-transient-attributes" rel="alternate" type="text/html" title="On concise test setups with FactoryBot" /><published>2024-10-26T00:00:00+00:00</published><updated>2024-10-26T00:00:00+00:00</updated><id>https://gergo.dev/factory-bot-transient-attributes</id><content type="html" xml:base="https://gergo.dev/factory-bot-transient-attributes"><![CDATA[<p>Recently, I reviewed a pull request where setting up the object under the test was rather cumbersome.
The setup spanned ten lines, each necessary to build a valid ActiveRecord object.
Having ten lines for setup is not necessarily a problem; however, most of the tests did not care about the translation details, and the setup was already duplicated in the same test file.</p>

<p>Here’s a snippet of how the file looked <em>before</em> and <em>after</em> the pull request changes.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># spec/models/product_spec.rb - Before</span>
<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="k">do</span>
  <span class="n">subject</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span> <span class="p">{</span> <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">it</span> <span class="s2">"is valid"</span> <span class="k">do</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">product</span><span class="p">).</span><span class="nf">to</span> <span class="n">be_valid</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># spec/models/product_spec.rb - After</span>
<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="k">do</span>
  <span class="n">subject</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">picture</span> <span class="o">=</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Test</span><span class="o">::</span><span class="no">UploadedFile</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"image.jpg"</span><span class="p">,</span> <span class="s2">"image/jpg"</span><span class="p">)</span>
    <span class="n">manual</span> <span class="o">=</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Test</span><span class="o">::</span><span class="no">UploadedFile</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"manual.pdf"</span><span class="p">,</span> <span class="s2">"application/pdf"</span><span class="p">)</span>

    <span class="n">product</span> <span class="o">=</span> <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">build</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span>
    <span class="n">product</span><span class="p">.</span><span class="nf">translations</span><span class="p">.</span><span class="nf">build</span><span class="p">(</span><span class="ss">locale: </span><span class="s2">"en"</span><span class="p">,</span> <span class="n">picture</span><span class="p">:,</span> <span class="n">manual</span><span class="p">:)</span>
    <span class="n">product</span><span class="p">.</span><span class="nf">translations</span><span class="p">.</span><span class="nf">build</span><span class="p">(</span><span class="ss">locale: </span><span class="s2">"fr"</span><span class="p">,</span> <span class="n">picture</span><span class="p">:,</span> <span class="n">manual</span><span class="p">:)</span>
    <span class="n">product</span><span class="p">.</span><span class="nf">translations</span><span class="p">.</span><span class="nf">build</span><span class="p">(</span><span class="ss">locale: </span><span class="s2">"hu"</span><span class="p">,</span> <span class="n">picture</span><span class="p">:,</span> <span class="n">manual</span><span class="p">:)</span>
    <span class="n">product</span><span class="p">.</span><span class="nf">save!</span>
    <span class="n">product</span>
  <span class="k">end</span>

  <span class="n">it</span> <span class="s2">"is valid"</span> <span class="k">do</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">product</span><span class="p">).</span><span class="nf">to</span> <span class="n">be_valid</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The test setup became cumbersome because additional validations were needed to reflect the business expectations.</p>

<p>The model represents a product with localized files containing the product image and the manual.
Previously, the product was valid without any translations, but now, at least one translation must be present with at least the picture.
This created a somewhat complex challenge for the factory definition.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # app/models/product.rb
 class Product &lt; ApplicationRecord
   translates :picture, :manual # Using the Globalize gem
   accepts_nested_attributes_for :translations

+  validates :translations, length: { minimum: 1 }
<span class="gi">+  validates_associated :translations
</span> end
</code></pre></div></div>

<!--
```mermaid
erDiagram
    PRODUCTS ||--|{ PRODUCT_TRANSLATIONS : translate
    PRODUCTS {
        int id
        string brand
        decimal price
    }
    PRODUCT_TRANSLATIONS {
        int product_id PK, FK
        string locale PK
        string picture "NOT NULL"
        string manual
    }
```
-->

<h2 id="solution">Solution</h2>

<p>The goal was to return to a simple but expressive one-liner from the overly verbose test setup.
After a couple of minutes of pairing, the following expression took shape to create the test objects.
It is explicit about the translations enough for the test requirements and allows further refinement when needed.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">subject</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span> <span class="p">{</span> <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">:product</span><span class="p">,</span> <span class="ss">with_translations: </span><span class="p">[</span><span class="ss">:en</span><span class="p">,</span> <span class="ss">:nl</span><span class="p">,</span> <span class="ss">:hu</span><span class="p">])}</span>
</code></pre></div></div>

<p>Because at least one translation is required, the base factory has to create a translation.
However, the localized content is not essential and is rarely checked.</p>

<p>I had the following considerations,</p>

<ul>
  <li>For the majority of cases, the translation details are irrelevant</li>
  <li>It should be easy to create none or multiple translations</li>
  <li>It should be easy to specify the details of the translations when needed</li>
</ul>

<p>With these in mind, I turned to the <a href="https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md#transient-attributes">transient attributes</a> of FactoryBot.
This feature allows for hiding the complexity of object creation for testing behind <em>transient</em> attributes.</p>

<blockquote>
  <p>Transient attributes are attributes only available within the factory definition, and not set on the object being built.</p>
</blockquote>

<p>I added an attribute containing a list of locale codes for which I wanted to generate translations.
This attribute allowed generating the required translations using this <em>transient</em> attribute in conjunction with the <code class="language-plaintext highlighter-rouge">translations_attributes</code> method defined by the <a href="https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html"><code class="language-plaintext highlighter-rouge">accepts_nested_attributes_for</code></a> method of ActiveRecord.</p>

<p>I set only a single locale code as a default, so the factory does the <a href="https://radanskoric.com/articles/test-factories-principal-of-minimal-defaults">minimum</a> to generate a valid object.
The list can be overridden using the <code class="language-plaintext highlighter-rouge">with_translations</code> attribute to generate a different set of translations, allowing the generation of none.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># spec/support/factories/products.rb</span>
<span class="no">FactoryBot</span><span class="p">.</span><span class="nf">define</span> <span class="k">do</span>
  <span class="n">factory</span> <span class="ss">:product</span> <span class="k">do</span>
    <span class="n">brand</span> <span class="p">{</span> <span class="s2">"ACME corp"</span> <span class="p">}</span>
    <span class="n">price</span> <span class="p">{</span> <span class="mf">10.99</span> <span class="p">}</span>

    <span class="n">transient</span> <span class="k">do</span>
      <span class="n">with_translations</span> <span class="p">{</span> <span class="sx">%i[en]</span> <span class="p">}</span>
    <span class="k">end</span>
    <span class="c1"># Defined by accepts_nested_attributes_for :translations</span>
    <span class="n">translations_attributes</span> <span class="k">do</span>
      <span class="n">picture</span> <span class="o">=</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Test</span><span class="o">::</span><span class="no">UploadedFile</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"sample.jpg"</span><span class="p">,</span> <span class="s2">"image/jpg"</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
      <span class="n">manual</span> <span class="o">=</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Test</span><span class="o">::</span><span class="no">UploadedFile</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"sample.pdf"</span><span class="p">,</span> <span class="s2">"application/pdf"</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>

      <span class="n">with_translations</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">locale</span><span class="o">|</span>
        <span class="p">{</span> <span class="n">locale</span><span class="p">:,</span> <span class="n">picture</span><span class="p">:,</span> <span class="ss">manual: </span><span class="p">}</span>
      <span class="k">end</span>
    <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>With the new factory definition, I could return to a single-liner to create the necessary objects without losing much of the explicitness of the test setup.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># spec/models/product_spec.rb - Before</span>
<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="k">do</span>
  <span class="n">subject</span><span class="p">(</span><span class="ss">:product</span><span class="p">)</span> <span class="p">{</span> <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">:product</span><span class="p">,</span> <span class="ss">with_translations: </span><span class="p">[</span><span class="ss">:en</span><span class="p">,</span> <span class="ss">:fr</span><span class="p">,</span> <span class="ss">:hu</span><span class="p">])</span> <span class="p">}</span>

  <span class="n">it</span> <span class="s2">"is valid"</span> <span class="k">do</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">product</span><span class="p">).</span><span class="nf">to</span> <span class="n">be_valid</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>In conclusion, FactoryBot showed its power again, helping me keep the test concise and flexible.
It is worth the time to glance through the examples of each FactoryBot feature to get an idea of when and where they can support writing excellent code.</p>

<!--
[Radan Skorić][1] wrote an article on [defaulting to minimal test factories][2]. In summary, the article suggests building factories that do the minimum to create valid objects. I fully agree with his idea, and arrived to the same conclusion over the years.

[1]: https://radanskoric.com/about/
-->]]></content><author><name>Gergő Sulymosi</name><email>gergo.sulymosi@gmail.com</email></author><category term="Testing" /><summary type="html"><![CDATA[Recently, I reviewed a pull request where setting up the object under the test was rather cumbersome. The setup spanned ten lines, each necessary to build a valid ActiveRecord object. Having ten lines for setup is not necessarily a problem; however, most of the tests did not care about the translation details, and the setup was already duplicated in the same test file.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gergo.dev/images/pebbles-johnson-wang.jpg" /><media:content medium="image" url="https://gergo.dev/images/pebbles-johnson-wang.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">You are missing errors in your Google Cloud Build steps</title><link href="https://gergo.dev/you-are-missing-errors-in-google-cloud-build-steps" rel="alternate" type="text/html" title="You are missing errors in your Google Cloud Build steps" /><published>2022-12-13T08:00:01+00:00</published><updated>2022-12-13T08:00:01+00:00</updated><id>https://gergo.dev/you-are-missing-errors-in-google-cloud-build-steps</id><content type="html" xml:base="https://gergo.dev/you-are-missing-errors-in-google-cloud-build-steps"><![CDATA[<p>If you are using <abbr title="Google Cloud Build">GCB</abbr>, you likely encountered cases where you needed to continue the process regardless of the result of a particular step.
Naturally, builds fail if a step terminates with a non-zero exit code.</p>

<p>Here is an example that pulls a container image. If the image does not exist, it’s okay to continue. Your steps may look different, more involved or use another command.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">steps</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">gcr.io/cloud-builders/docker'</span>
    <span class="na">entrypoint</span><span class="pi">:</span> <span class="s1">'</span><span class="s">bash'</span>
    <span class="na">args</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">-c'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">docker</span><span class="nv"> </span><span class="s">pull</span><span class="nv"> </span><span class="s">eu.gcr.io/project/myimage:$TAG</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">exit</span><span class="nv"> </span><span class="s">0'</span><span class="pi">]</span>
</code></pre></div></div>

<blockquote>
  <p><em>If your step hides if it fails because it is OK to let it fail</em>, I have a better option for you.</p>
</blockquote>

<h2 id="what-is-the-problem-with-the-code-above">What is the problem with the code above?</h2>
<p>There are two problems with ignoring the exit code of the step.</p>

<ol>
  <li>The step will lie about the exit code, and you will not be able to see when the command fails. Seeing an always passing step provides a <em>false assurance</em> that everything is fine.</li>
  <li>The step is convoluted by the error handling code. It is no longer possible to glance at the <code class="language-plaintext highlighter-rouge">args</code> and know what the step will do.</li>
</ol>

<h2 id="let-it-fail">Let it fail</h2>
<p>The Google Cloud Build <a href="https://cloud.google.com/build/docs/build-config-file-schema#allowfailure">configuration schema</a> has a ton of underutilised features.</p>

<p>There are two handy features for this situation. <em>Please note that both of them are experimental.</em></p>

<ul>
  <li><a href="https://cloud.google.com/build/docs/build-config-file-schema#allowfailure"><code class="language-plaintext highlighter-rouge">allowFailure: &lt;boolean&gt;</code></a></li>
  <li><a href="https://cloud.google.com/build/docs/build-config-file-schema#allowexitcodes"><code class="language-plaintext highlighter-rouge">allowExitCodes: &lt;array|string&gt;</code></a></li>
</ul>

<p>These options allow you to tell the runner to ignore if the step fails. With the latter, you can even specify which error codes are allowed.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">steps</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">gcr.io/cloud-builders/docker'</span>
    <span class="na">args</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">pull'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">eu.gcr.io/project/myimage:$TAG'</span><span class="pi">]</span>
    <span class="na">allowFailure</span><span class="pi">:</span> <span class="no">true</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">gcr.io/cloud-builders/docker'</span>
    <span class="na">args</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">pull'</span><span class="pi">,</span> <span class="s1">'</span><span class="s">eu.gcr.io/project/myimage:$TAG'</span><span class="pi">]</span>
    <span class="na">allowExitCodes</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">1</span><span class="pi">]</span>
</code></pre></div></div>

<h2 id="alternatives">Alternatives</h2>
<p>If this solution is not for you, it may be worth looking at the <a href="https://cloud.google.com/build/docs/build-config-file-schema#script"><code class="language-plaintext highlighter-rouge">script</code></a> property and how you can use it to <a href="https://cloud.google.com/build/docs/configuring-builds/run-bash-scripts#using_the_script_field">run bash scripts</a> .</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">steps</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">gcr.io/cloud-builders/docker'</span>
    <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">docker pull eu.gcr.io/project/myimage:$TAG || exit 0</span>
</code></pre></div></div>]]></content><author><name>Gergő Sulymosi</name><email>gergo.sulymosi@gmail.com</email></author><category term="CI/CD" /><summary type="html"><![CDATA[If you are using Google Cloud Build, you likely encountered cases where you needed to continue the process regardless of the result of a particular step. If your step hides if it fails because it is OK to let it fail, I have a better option for you.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gergo.dev/images/litter-vova-drozdey.jpg" /><media:content medium="image" url="https://gergo.dev/images/litter-vova-drozdey.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>