<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
	<channel>
		<title>Author at Infinum</title>
		<atom:link href="https://infinum.com/blog/author/tamara-andonovska/feed/" rel="self" type="application/rss+xml" />
		<link></link>
		<description>Building digital products</description>
		<lastBuildDate>Thu, 30 Apr 2026 13:44:05 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>50640https://infinum.com/uploads/2024/02/Snapshot-hero-min.webp</url>
				</image>
				<title>Snapshot Testing – Capturing Your Code’s Best Side</title>
				<link>https://infinum.com/blog/snapshot-testing/</link>
				<pubDate>Fri, 16 Feb 2024 13:18:44 +0000</pubDate>
				<dc:creator>Tamara Andonovska</dc:creator>
				<guid isPermaLink="false">https://infinum.com/?p=50640</guid>
				<description>
					<![CDATA[<p>Find out why, when, and how to use snapshot testing – a simple way of comparing different states of the UI, especially useful when refactoring code.  </p>
<p>The post <a href="https://infinum.com/blog/snapshot-testing/">Snapshot Testing – Capturing Your Code’s Best Side</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-175"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-92">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-95"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-93">
	<p	class='typography typography--size-36-text js-typography block-paragraph__paragraph'
	data-id='es-94'
	>
	<strong>Snapshot testing is a simple way of comparing different states of the user interface, especially useful when refactoring code. Find out why, when, and how to use it.&nbsp;</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-98"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-96">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-97'
	>
	Did you know that views can also take selfies? Yes, you read that correctly. Snapshot testing offers a simple way of capturing the current state of the user interface, proving to be incredibly useful in a number of situations in the development process.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-101"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-99">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-100'
	>
	While snapshot tests are sometimes mistaken for UI testing, they serve different purposes. Snapshot tests are designed to assess how content is displayed across various setups and screen sizes. On the other hand, UI tests focus on what is displayed on the screen.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-104"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-102">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-103'
	>
	It&#8217;s important to note that snapshot tests do not replace other forms of testing, such as unit or integration tests. Instead, they complement them by tracking visual changes in applications and act as a safety net during the process of refactoring UI code.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-107"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-105">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-106'
	>
	In this article, we’ll explore the realm of snapshot testing, including when and why it’s beneficial, its most common use cases, and provide a detailed guide on incorporating snapshot tests into your project.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-110"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-108">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-109'
	>
	The secret magic of snapshot testing</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-113"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-111">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-112'
	>
	You might be wondering how snapshot testing works. Is it some sort of wizardry? It depends on your perspective, but it’s actually based on very straightforward principles.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-116"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-114">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-115'
	>
	The process takes place in three phases:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-121"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--infinum block-bullet__bullet" data-id="es-117">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-118'
	>
	1</p>	<div class="bullet__content">
		<h3	class='typography typography--size-20-text js-typography bullet__heading'
	data-id='es-119'
	>
	<strong>Recording the reference image.</strong> </h3><p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-120'
	>
	A reference image or snapshot is a screenshot of the UI in a known good state.</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-126"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--infinum block-bullet__bullet" data-id="es-122">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-123'
	>
	2</p>	<div class="bullet__content">
		<h3	class='typography typography--size-20-text js-typography bullet__heading'
	data-id='es-124'
	>
	<strong>Comparing the snapshot with the current state of the UI with a previous reference image.</strong></h3><p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-125'
	>
	This comparison is done pixel by pixel.</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-131"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--infinum block-bullet__bullet" data-id="es-127">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-128'
	>
	3</p>	<div class="bullet__content">
		<h3	class='typography typography--size-20-text js-typography bullet__heading'
	data-id='es-129'
	>
	<strong>The reference image either passes or fails the test</strong>. </h3><p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-130'
	>
	A test is considered passed if the pixels of the new screenshot match those from the previous snapshot.</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-134"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-132">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-133'
	>
	We&#8217;ll delve deeper into the specifics of this comparison later on, including how closely these images need to match and how to make the appropriate adjustments (spoiler alert: it doesn’t have to be a 100% match).</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-137"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-135">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-136'
	>
	How to write snapshot tests</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-140"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-138">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-139'
	>
	Writing tests for SwiftUI and UIKit views</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-143"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-141">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-142'
	>
	Before we get into writing the tests, we need to import the library that enables this, <a href="https://github.com/pointfreeco/swift-snapshot-testing" target="_blank" rel="noreferrer noopener">swift-snapshot-testing</a>. It doesn’t matter whether your project is using SPM or CocoaPods, the library is very easy to add by following the steps of the repo’s README.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-146"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-144">
	<h4	class='typography typography--size-30-text js-typography block-heading__heading'
	data-id='es-145'
	>
	Creating the test class</h4></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-149"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-147">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-148'
	>
	The setup for creating snapshot tests is very straightforward. The first step is creating a test class. According to best practices, the name of that class should be the name of the class we are testing appended with the keyword <code>Tests</code>. Our test class needs to inherit from the <code>XCTestCase</code> class to facilitate testing.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-152"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-150">
	<h4	class='typography typography--size-30-text js-typography block-heading__heading'
	data-id='es-151'
	>
	Writing test cases</h4></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-155"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-153">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-154'
	>
	The test class can contain as many methods as we need to cover all test cases. In each method, we need to instantiate the view in a way appropriate for the case. We then check every test case with the help of the <code>assertSnapshot</code> method provided by the snapshotting library. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-157"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">public</span><span class="token"> </span><span class="token">func assertSnapshot</span><span class="token">&lt;</span><span class="token" style="color: #005cc5;">Value</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #005cc5;">Format</span><span class="token">&gt;</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">of value</span><span class="token">: </span><span class="token" style="color: #d73a49;">@autoclosure</span><span class="token"> </span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">throws</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token" style="color: #005cc5;">Value</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">as snapshotting</span><span class="token">: </span><span class="token">Snapshotting</span><span class="token">&lt;</span><span class="token" style="color: #005cc5;">Value</span><span class="token">, Format</span><span class="token">&gt;</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">named name</span><span class="token">: </span><span class="token" style="color: #005cc5;">String</span><span class="token" style="color: #d73a49;">?</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">nil</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">record recording</span><span class="token">: </span><span class="token" style="color: #005cc5;">Bool</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">timeout</span><span class="token">: </span><span class="token">TimeInterval </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">5</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">file</span><span class="token">: </span><span class="token" style="color: #005cc5;">StaticString</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#file</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">testName</span><span class="token">: </span><span class="token" style="color: #005cc5;">String</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#function</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">line</span><span class="token">: </span><span class="token" style="color: #005cc5;">UInt</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#line</span><span class="token">
</span></span><span class="line"><span class="token">)</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-160"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-158">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-159'
	>
	As we can see by the method’s signature, the only two required parameters are <code>of value</code>, the view we are checking for accuracy and consistency, and <code>as snapshotting</code>, which allows us to set the precision with which the view is supposed to match the reference image. The rest of the parameters have default values that we can change on a case-to-case basis.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-163"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-161">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-162'
	>
	Here’s how we would write a test case for an <code>AlertSwiftUIView</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-165"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class AlertSwiftUIViewTests</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func testAlertGeneralSetup</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= AlertSw</span><span class="token">iftUIView(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">item:</span><span class="token"> .</span><span class="token" style="color: #d73a49;">init</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">                </span><span class="token">image:</span><span class="token"> .</span><span class="token">MyProject</span><span class="token">.</span><span class="token">Images</span><span class="token">.</span><span class="token">Illustrations</span><span class="token">.</span><span class="token">success</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">title:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">My success title!</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">description:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Very successful and nice description to celebrate your success.</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">buttons:</span><span class="token"> [
</span></span><span class="line"><span class="token">                    .</span><span class="token" style="color: #d73a49;">init</span><span class="token">(</span><span class="token">title:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CLOSE</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">, </span><span class="token">theme:</span><span class="token"> .</span><span class="token">primary</span><span class="token">, </span><span class="token">action:</span><span class="token"> </span><span class="token" style="color: #005cc5;">nil</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">                </span><span class="token">]
</span></span><span class="line"><span class="token">            </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">frame(</span><span class="token">width:</span><span class="token"> </span><span class="token" style="color: #005cc5;">375</span><span class="token">, </span><span class="token">height:</span><span class="token"> </span><span class="token" style="color: #005cc5;">600</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">background(</span><span class="token">.</span><span class="token">black</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">of:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">precision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.98</span><span class="token">, </span><span class="token">perceptualPrecision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.99</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-168"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-166">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-167'
	>
	The same test case for the same view written in UIKit would look pretty similar.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-170"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class AlertViewTests</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func testAlertGeneralSetup</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= AlertVi</span><span class="token">ew(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        view.</span><span class="token">configure(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">with:</span><span class="token"> .</span><span class="token" style="color: #d73a49;">init</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">                </span><span class="token">image:</span><span class="token"> .</span><span class="token">MyProject</span><span class="token">.</span><span class="token">Images</span><span class="token">.</span><span class="token">Illustrations</span><span class="token">.</span><span class="token">success</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">title:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">My success title!</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">description:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Very successful and nice description to celebrate your success.</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">buttons:</span><span class="token"> [
</span></span><span class="line"><span class="token">                    .</span><span class="token" style="color: #d73a49;">init</span><span class="token">(</span><span class="token">title:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CLOSE</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">, </span><span class="token">theme:</span><span class="token"> .</span><span class="token">primary</span><span class="token">, </span><span class="token">action:</span><span class="token"> </span><span class="token" style="color: #005cc5;">nil</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">                </span><span class="token">]
</span></span><span class="line"><span class="token">            </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        view.</span><span class="token">backgroundColor</span><span class="token"> </span><span class="token" style="color: #d73a49;">= .black
</span></span><span class="line"><span class="token">        
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">of:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">                </span><span class="token">precision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.98</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">perceptualPrecision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.99</span><span class="token">,
</span></span><span class="line"><span class="token">                </span><span class="token">size:</span><span class="token"> .</span><span class="token" style="color: #d73a49;">init</span><span class="token">(</span><span class="token">width:</span><span class="token"> </span><span class="token" style="color: #005cc5;">375</span><span class="token">, </span><span class="token">height:</span><span class="token"> </span><span class="token" style="color: #005cc5;">600</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-173"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-171">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-172'
	>
	It’s important to note that if you don’t specify the <code>size</code> parameter when generating the reference image for testing a UIKit view, the snapshot will not be recorded. Instead, we’ll get an image that displays the following error message:</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-178"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-176"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-177">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/02/Screenshot_2024-02-13_at_2.37.04_PM.webp"
					class="image__img block-media__image-img"
					alt=""
										height="404"
															width="1312"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-193"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-179">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-182"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-180">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-181'
	>
	On the other hand, the snapshot image of a SwiftUI view will potentially appear in weird dimensions but still be recorded.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-185"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-183">
	<h4	class='typography typography--size-30-text js-typography block-heading__heading'
	data-id='es-184'
	>
	Generating the reference image</h4></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-188"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-186">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-187'
	>
	As we explained above, snapshot testing compares the current state of the user interface to a saved reference snapshot of it. Now, unless we have a reference image stored somewhere first, we have nothing to compare our current state with.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-191"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-189">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-190'
	>
	The first time we run these tests, we want to provide a <code>record</code> parameter to the <code>assertSnapshot</code> method and set it to <code>true</code>. This parameter generates and saves a reference snapshot. This way, we can keep running our tests and checking against this reference.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-196"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-194"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-195">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/02/Screenshot_2024-02-14_at_3.06.36_PM.webp"
					class="image__img block-media__image-img"
					alt=""
										height="142"
															width="620"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-207"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-197">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-200"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-198">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-199'
	>
	The cool thing about this is that if you forget to add this parameter the first time you try running the test and <code>assertSnapshot</code> fails to find the reference, it will create it for you regardless. However, we want to do this explicitly to avoid any random mistakes.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-203"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-201">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-202'
	>
	<strong>Important note</strong>: Make sure to remove the <code>record</code> parameter after running the tests for the first time. The tests will fail until you run them without the record mode on.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-205"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">of:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">precision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.98</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">perceptualPrecision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.99</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">)</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">record:</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token">)</span><span class="token">
</span></span></code></pre></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-210"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-208"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-209">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2024/02/Mobile_screenshot-1.webp				media='(max-width: 699px)'
				type=image/webp								height="724"
												width="840"
				 />
												<img
					src="https://infinum.com/uploads/2024/02/Screenshot_2024-02-13_at_1.19.53_PM.webp"
					class="image__img block-media__image-img"
					alt=""
										height="209"
															width="1547"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-280"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-211">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-214"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-212">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-213'
	>
	Writing tests for SwiftUI VIPER views</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-217"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-215">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-216'
	>
	We’ve looked into writing test cases for views that were components, meaning they only took part of the screen. What if we wanted to test how the whole UI behaves when displayed on different devices with different screen sizes?</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-220"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-218">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-219'
	>
	We tried out this scenario with SwiftUI views. When talking about views in this section, we are referring to the view component from the VIPER architecture since it’s our architecture of choice at Infinum. This also means that these views consist of multiple view components that we tested above.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-223"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-221">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-222'
	>
	Creating a test for these views is even simpler than the ones for testing components. We only need to initialize the view and then use the <code>assertSnapshot</code> method provided by the snapshotting library to check how the view looks on a smaller or bigger layout.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-226"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-224">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-225'
	>
	The <code>record</code> parameter is used only the first time we run the test so that a reference snapshot is saved. After that, we need to rerun the test with that line of code removed, just like we did when testing the components.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-228"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class PrepareForSetupViewTests</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">let</span><span class="token"> interactor </span><span class="token" style="color: #d73a49;">= Pre</span><span class="token">pareForSetupInteractor(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">let</span><span class="token"> wireframe </span><span class="token" style="color: #d73a49;">= Pre</span><span class="token">pareForSetupWireframe(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">lazy</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> presenter </span><span class="token" style="color: #d73a49;">= Pre</span><span class="token">pareForSetupPresenter(</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">interactor:</span><span class="token"> interactor</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">wireframe:</span><span class="token"> wireframe
</span></span><span class="line"><span class="token">    </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func testPrepareForSetupView</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= Prepare</span><span class="token">ForSetupView(</span><span class="token">presenter:</span><span class="token"> presenter</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">matching:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">layout:</span><span class="token"> bigLayout</span><span class="token">)</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">named:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">iPhone13ProMax</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">record:</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">matching:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">layout:</span><span class="token"> smallLayout</span><span class="token">)</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">named:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">iPhoneSe</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">record:</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-231"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-229">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-230'
	>
	The <code>bigLayout</code> and <code>smallLayout</code> are custom layout configurations representing a bigger and smaller screen size.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-233"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">var smallLayout:</span><span class="token"> SwiftUISnapshotLayout </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">var layout:</span><span class="token"> ViewImageConfig </span><span class="token" style="color: #d73a49;">= .iP</span><span class="token">honeSe(</span><span class="token">.</span><span class="token">portrait</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    layout.</span><span class="token">safeArea</span><span class="token"> </span><span class="token" style="color: #d73a49;">= .ze</span><span class="token">ro</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">return</span><span class="token"> .</span><span class="token">device(</span><span class="token">config:</span><span class="token"> layout</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">var bigLayout:</span><span class="token"> SwiftUISnapshotLayout </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">var layout:</span><span class="token"> ViewImageConfig </span><span class="token" style="color: #d73a49;">= .iP</span><span class="token">hone13ProMax(</span><span class="token">.</span><span class="token">portrait</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    layout.</span><span class="token">safeArea</span><span class="token"> </span><span class="token" style="color: #d73a49;">= .ze</span><span class="token">ro</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">return</span><span class="token"> .</span><span class="token">device(</span><span class="token">config:</span><span class="token"> layout</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-236"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-234">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-235'
	>
	How we rewrote UIKit views in SwiftUI – a case study</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-239"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-237">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-238'
	>
	We worked on a project that included a number of UI components that were used as building blocks for constructing large screens. From the perspective of the screen using the components, a component is just a simple <code>UIView</code>. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-242"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-240">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-241'
	>
	We decided to do a major rewrite of all of the components where we wanted to use SwiftUI for implementing them. However, there was a catch. We had to rewrite more than 50 components in SwiftUI. With a tight deadline, we didn’t want to make any mistakes in the rewrite process. Snapshot tests came to the rescue.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-245"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-243">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-244'
	>
	We were able to generate snapshots of the components in their initial state and then wanted to capture snapshots of the same component after the rewrite. If the tests failed, we would know that the rewritten component had a different visual behavior, which would mean the rewrite wasn’t successful.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-248"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-246">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-247'
	>
	However, there is a difference in how you create snapshot tests for UIKit and SwiftUI views, and we didn’t want to change the tests’ implementation after we rewrote the component to SwiftUI. The idea was to have the same code that generated the snapshots for UIKit to now generate the snapshots for the SwiftUI view.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-251"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-249">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-250'
	>
	Here’s an interesting fact about the architecture of SwiftUI and UIKit components – they don’t differ at all. We could have the exact same protocol defining the <code>ComponentCreator</code> for both SwiftUI and UIKit components.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-253"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">protocol ComponentCreator</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func createView</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">UIView</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-256"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-254">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-255'
	>
	The caller of the <code>ComponentCreator</code> class was still implemented in UIKit. For that reason, we needed to supply a UIKit view to the caller, even if the component was written in SwiftUI. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-259"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-257">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-258'
	>
	Thanks to this approach, we were able to get both SwiftUI and UIKit components at the same time.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-262"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-260">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-261'
	>
	But how did we manage to create a UIView instance from a SwiftUI view? The implementation is quite simple, so let&#8217;s check out a concrete <code>ComponentCreator</code> class that implements the <code>createView</code> method:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-264"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">func createView</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">UIView </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">let</span><span class="token"> exampleComponentView </span><span class="token" style="color: #d73a49;">= E</span><span class="token">xampleComponent(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #6a737d;">//</span><span class="token" style="color: #6a737d;"> SwiftUI view</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">let</span><span class="token"> hostingController </span><span class="token" style="color: #d73a49;">= U</span><span class="token">IHostingController(</span><span class="token">rootView:</span><span class="token"> exampleComponentView</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">return</span><span class="token"> hostingController.</span><span class="token" style="color: #005cc5;">view</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-267"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-265">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-266'
	>
	It worked! The view was successfully created by the caller, and the implementation seemed to work correctly.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-270"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-268">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-269'
	>
	To return to the snapshot tests, let&#8217;s give an example of a component going through a rewrite.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-273"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-271">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-272'
	>
	First, we have a component that is implemented using UIKit with the name <code>ExampleComponent</code>. Let&#8217;s create some snapshots. For now, we will be using the default <code>assertSnapshot</code> method provided by the library.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-275"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class ExampleComponentSnapshotTests</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func testExampleComponent</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> creator </span><span class="token" style="color: #d73a49;">= Exa</span><span class="token">mpleComponentCreator(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= cre</span><span class="token">ator.</span><span class="token">createView(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">matching:</span><span class="token"> view</span><span class="token">, </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">, </span><span class="token">record:</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-278"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-276">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-277'
	>
	Snapshot:</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-283"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-281"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-282">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2024/02/Example_component-1400x204.webp				media='(max-width: 699px)'
				type=image/webp								height="204"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2024/02/Example_component.webp"
					class="image__img block-media__image-img"
					alt=""
										height="292"
															width="2000"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-292"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-284">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-287"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-285">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-286'
	>
	Let&#8217;s rewrite the component in SwiftUI, remove the <code>record</code> flag from <code>assertSnapshot</code> inside the test method, and see if the rewrite was successful.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-290"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-288">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-289'
	>
	Unfortunately, this test has failed, and if we look at the snapshot, the component isn’t even remotely close to the original snapshot or the implementation.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-295"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-293"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-294">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/02/Square.webp"
					class="image__img block-media__image-img"
					alt=""
										height="1232"
															width="1040"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-334"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-296">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-299"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-297">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-298'
	>
	To fix this issue, we decided to recreate the screen that the component is displayed on but in a test environment since the component looks good when we add it to a regular <code>UIViewController</code>. The assumption is that it will also look good in the snapshot when we recreate this behavior.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-302"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-300">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-301'
	>
	Let’s add two new methods:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-304"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">extension</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">enum Constants</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">static</span><span class="token"> </span><span class="token" style="color: #d73a49;">let</span><span class="token"> iphone14Size </span><span class="token" style="color: #d73a49;">= CGS</span><span class="token">ize(</span><span class="token">width:</span><span class="token"> </span><span class="token" style="color: #005cc5;">390</span><span class="token">, </span><span class="token">height:</span><span class="token"> </span><span class="token" style="color: #005cc5;">844</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">func createScreenshotTestView</span><span class="token">(</span><span class="token">creator</span><span class="token">: </span><span class="token">ComponentCreator</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">UIView </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= cre</span><span class="token">ator.</span><span class="token">createView(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> containerView </span><span class="token" style="color: #d73a49;">= UIS</span><span class="token">tackView(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        containerView.</span><span class="token">addArrangedSubview(</span><span class="token">view</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> scrollView </span><span class="token" style="color: #d73a49;">= UIS</span><span class="token">crollView(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        scrollView.</span><span class="token">addSubview(</span><span class="token">containerView</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        scrollView.</span><span class="token">backgroundColor</span><span class="token"> </span><span class="token" style="color: #d73a49;">= .wh</span><span class="token">ite</span><span class="token">
</span></span><span class="line"><span class="token">        containerView.</span><span class="token">widthAnchor</span><span class="token">.</span><span class="token">constraint(</span><span class="token">equalTo:</span><span class="token"> scrollView.</span><span class="token">widthAnchor</span><span class="token">)</span><span class="token">.</span><span class="token">isActive</span><span class="token"> </span><span class="token" style="color: #d73a49;">= tru</span><span class="token" style="color: #005cc5;">e</span><span class="token">
</span></span><span class="line"><span class="token">        containerView.</span><span class="token">pinToSuperview(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        scrollView.</span><span class="token">frame</span><span class="token"> </span><span class="token" style="color: #d73a49;">= CGR</span><span class="token">ect(</span><span class="token">x:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</span><span class="token">, </span><span class="token">y:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</span><span class="token">, </span><span class="token">width:</span><span class="token"> Constants.</span><span class="token">iphone14Size</span><span class="token">.</span><span class="token">width</span><span class="token">, </span><span class="token">height:</span><span class="token"> Constants.</span><span class="token">iphone14Size</span><span class="token">.</span><span class="token">height</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        scrollView.</span><span class="token">setNeedsLayout(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        scrollView.</span><span class="token">layoutIfNeeded(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">return</span><span class="token"> scrollView
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func assertComponentSnapshot</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">record recording</span><span class="token">: </span><span class="token" style="color: #005cc5;">Bool</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">file</span><span class="token">: </span><span class="token" style="color: #005cc5;">StaticString</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#file</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">testName</span><span class="token">: </span><span class="token" style="color: #005cc5;">String</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#function</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">line</span><span class="token">: </span><span class="token" style="color: #005cc5;">UInt</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">#line</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">creator</span><span class="token">: </span><span class="token">ComponentCreator</span><span class="token">,
</span></span><span class="line"><span class="token">        </span><span class="token">precision</span><span class="token">: </span><span class="token" style="color: #005cc5;">Float</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">1</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">let</span><span class="token"> view </span><span class="token" style="color: #d73a49;">= cre</span><span class="token">ateScreenshotTestView(</span><span class="token">creator:</span><span class="token"> creator</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">matching:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">precision:</span><span class="token"> precision</span><span class="token">)</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">record:</span><span class="token"> recording</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">file:</span><span class="token"> file</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">testName:</span><span class="token"> testName</span><span class="token">,
</span></span><span class="line"><span class="token">            </span><span class="token">line:</span><span class="token"> line
</span></span><span class="line"><span class="token">        </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-307"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-305">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-306'
	>
	When we have recreated the environment where we use the components that we are rewriting, we need to create a new snapshot of the original UIKit component. This way, when we rewrite the component in SwiftUI, we don’t need to change a single line of code. We can just run the tests again and check if the rewrite was successful. Now the SwiftUI and UIKit views should behave the same.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-310"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-308">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-309'
	>
	What if your views include remote images?</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-313"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-311">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-312'
	>
	In iOS development, we often use images that are stored on a remote server. This could cause problems when generating snapshot tests since we don’t want to do remote resource fetching in our test environment. For example, a popular framework for fetching remote images in iOS is Kingfisher.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-316"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-314">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-315'
	>
	What would be the easiest way to handle remote images in the test environment? Let&#8217;s illustrate this with an example.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-319"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-317">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-318'
	>
	Let&#8217;s suppose we have an image of the globe hosted on Imgur and a view that fetches the image using Kingfisher:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-321"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">struct ExampleComponentView</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">View </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">var body:</span><span class="token"> </span><span class="token" style="color: #d73a49;">some</span><span class="token"> View </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">VStack</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">Text(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Hello, World!</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">            </span><span class="token">KFImage(</span><span class="token">URL(</span><span class="token">string:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">https://i.imgur.com/hAgfcId.png</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">                .</span><span class="token">resizable(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">                .</span><span class="token">frame(</span><span class="token">width:</span><span class="token"> </span><span class="token" style="color: #005cc5;">100</span><span class="token">, </span><span class="token">height:</span><span class="token"> </span><span class="token" style="color: #005cc5;">100</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-324"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-322">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-323'
	>
	Let’s also add this image to the local resources. If you decide to implement this on your project, you can use any mock image for snapshot tests. For this example, we will use the same image that is hosted on Imgur.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-327"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-325">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-326'
	>
	We are going to manually manipulate the Kingfisher cache and store the local mock image for the URL of the hosted image. This will prevent Kingfisher from fetching the image.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-329"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class ExampleComponentSnapshotTests</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #6f42c1;">XCTestCase </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">enum Constants</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">static</span><span class="token"> </span><span class="token" style="color: #d73a49;">let</span><span class="token"> mockImageUrl </span><span class="token" style="color: #d73a49;">= &quot;ht</span><span class="token" style="color: #032f62;">tps://i.imgur.com/hAgfcId.png</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #d73a49;">static</span><span class="token"> </span><span class="token" style="color: #d73a49;">let</span><span class="token"> mockImage </span><span class="token" style="color: #d73a49;">= UII</span><span class="token">mage.</span><span class="token">world</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token" style="color: #d73a49;">class</span><span class="token"> </span><span class="token">func setUp</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #005cc5;">super</span><span class="token">.</span><span class="token">setUp(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">        KingfisherManager
</span></span><span class="line"><span class="token">            .</span><span class="token">shared</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">cache</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">memoryStorage</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">store(</span><span class="token">value:</span><span class="token"> Constants.</span><span class="token">mockImage</span><span class="token">, </span><span class="token">forKey:</span><span class="token"> Constants.</span><span class="token">mockImageUrl</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">func testExampleComponent</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">assertComponentSnapshot(</span><span class="token">record:</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">, </span><span class="token">creator:</span><span class="token"> </span><span class="token">ExampleComponentCreator(</span><span class="token">)</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token" style="color: #d73a49;">class</span><span class="token"> </span><span class="token">func tearDown</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        KingfisherManager
</span></span><span class="line"><span class="token">            .</span><span class="token">shared</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">cache</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token">memoryStorage</span><span class="token">
</span></span><span class="line"><span class="token">            .</span><span class="token" style="color: #005cc5;">remove</span><span class="token">(</span><span class="token">forKey:</span><span class="token"> Constants.</span><span class="token">mockImageUrl</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-332"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-330">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-331'
	>
	This implementation results in the following snapshot:</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-337"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-335"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-336">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2024/02/Hello_World-1400x1405.webp				media='(max-width: 699px)'
				type=image/webp								height="1405"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2024/02/Hello_World.webp"
					class="image__img block-media__image-img"
					alt=""
										height="2056"
															width="2048"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-398"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-338">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-341"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-339">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-340'
	>
	The precision trap in snapshot testing</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-344"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-342">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-343'
	>
	As we mentioned above, snapshot tests can pass even when the new capture isn’t pixel-identical to the reference. You might be wondering how this works. The answer lies in the parameters we purposely didn’t go into until now: precision and perceptual precision.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-346"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-highlighted-text">
	<p	class='typography typography--size-36-text js-typography block-highlighted-text__typography'
	data-id='es-345'
	>
	<strong>A snapshot test can fail or pass depending on how you set the precision parameters so it’s important to carefully choose their values.</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-349"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-347">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-348'
	>
	By default, precision is set to 1 or 100%, meaning each pixel of the capture should match each pixel of the reference. However, in some cases, we may not want to go for 100% precision because it might be causing our tests to fail.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-352"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-350">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-351'
	>
	For example, a common reason for failing tests are minor differences in interfaces arising from different architectures in hybrid teams using both Apple silicon and Intel machines or due to anti-aliasing. To avoid this, we can decrease the precision by 0.01-0.04, depending on how much we need these failing tests (that are fundamentally matching) to pass.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-355"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-353">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-354'
	>
	According to the library&#8217;s documentation, perceptual precision is “the percentage a pixel must match the source pixel to be considered a match”. This actually means that the pixels get compared on the basis of how similar they are according to the human eye, that is, if the human eye can see the difference between the pixel from the newly made snapshot and the reference image. The precision of the human eye is between 98-99%. For that reason, we can safely use 0.99, i.e., 99%, for the perceptual precision parameter.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-357"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-swift github-light" data-language="swift" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">assertSnapshot(</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">of:</span><span class="token"> view</span><span class="token">,
</span></span><span class="line"><span class="token">    </span><span class="token">as:</span><span class="token"> .</span><span class="token" style="color: #005cc5;">image</span><span class="token">(</span><span class="token">precision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.98</span><span class="token">, </span><span class="token">perceptualPrecision:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0.99</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">)</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-360"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-358">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-359'
	>
	Important note: Pay attention to your failing tests and make sure the reason they are failing is really some factor like those mentioned above and not a human error. If you find yourself decreasing the precision to less than 95%, you might want to ask yourself if you’re doing something wrong. <strong>It’s okay to slightly tweak the precision. It is not okay to completely invalidate the purpose of snapshot tests.</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-363"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-361">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-362'
	>
	When to use snapshot testing</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-366"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-364">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-365'
	>
	You might be wondering if snapshot tests are a good fit for your project. The answer is – it depends. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-369"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-367">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-368'
	>
	If you only have static views that don’t change visually, snapshot testing capabilities could be a great way of confirming that changes to the implementation don’t cause the view to change visually. Also, if you are considering rewriting your UIKit views in SwiftUI like in our example above, then you are in for a treat. Snapshot tests will work great for you. You can create snapshots of the UIKit view before the rewrite and then compare the rewritten view in SwiftUI with the old UIKit snapshot. If the test succeeds, you know you did a good job since the component didn’t change visually.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-372"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-370">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-371'
	>
	On the other hand, if you constantly make changes to your views and you’re not planning any kind of refactoring, snapshot testing doesn’t make much sense because the tests will constantly fail and give you headaches. You will constantly need to update the snapshots just so your tests don’t fail.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-375"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-373">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-374'
	>
	Another thing to consider is that snapshot testing requires you to include one more third-party library in your project because there are no native solutions for implementing snapshot tests.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-378"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-376">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-377'
	>
	<strong>Pros:</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-381"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-379">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-380'
	>
	<li>Snapshot tests are great for UIKit to SwiftUI refactors since they will confirm that the views didn’t visually change.</li><li>They catch unwanted visual changes in views. This is especially useful if you have a lot of views that shouldn’t change and you are doing some implementation changes in them.</li><li>Easy to set up, especially if you follow this blog post.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-384"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-382">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-383'
	>
	<strong>Cons:</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-387"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-385">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-386'
	>
	<li>They can give you a hard time if you need to visually change your views often. They will constantly fail, and you will need to create new snapshots.</li><li>One more third-party dependency that needs to be added to the project.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-390"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-388">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-389'
	>
	Snapshot testing – a great asset to be used with care</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-393"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-391">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-392'
	>
	Snapshot tests are very easy to set up and even easier to write. They provide important information about the look and consistency of our UI and therefore provide additional value, especially when combined with other types of tests. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-396"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-394">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-395'
	>
	However, you should assess whether adding these tests to your project is truly worth it. If the development is very dynamic and the UI is constantly changing, we recommend skipping them. We don’t want to spend more time modifying snapshot tests so that our builds pass successfully instead of having them show us that our UI is actually broken. So stay ahead of the curve with snapshot testing capabilities, but remember to use them with care.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-401"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-399">
	</div>

<div class="block-blog-content-main">
	</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/snapshot-testing/">Snapshot Testing – Capturing Your Code’s Best Side</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>