<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abdelrahman Soltan's blog]]></title><description><![CDATA[Abdelrahman Soltan's blog]]></description><link>https://abdelrahmansoltan.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 18 Jun 2026 21:33:41 GMT</lastBuildDate><atom:link href="https://abdelrahmansoltan.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Shrink Before You Send: Frontend Image Resizing for Faster and Cheaper Uploads]]></title><description><![CDATA[Context
The Problem with Large Image Uploads
Modern web applications often handle large image uploads, especially from mobile devices that capture high-resolution photos by default. Uploading these full-sized images directly to the backend or sending...]]></description><link>https://abdelrahmansoltan.hashnode.dev/frontend-image-resizing</link><guid isPermaLink="true">https://abdelrahmansoltan.hashnode.dev/frontend-image-resizing</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[frontend]]></category><category><![CDATA[canvas]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Abdelrahman Soltan]]></dc:creator><pubDate>Sun, 09 Nov 2025 11:45:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762633570298/ac70d9ed-58cc-434f-affd-87e9ed05ff05.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-context">Context</h2>
<h3 id="heading-the-problem-with-large-image-uploads"><strong>The Problem with Large Image Uploads</strong></h3>
<p>Modern web applications often handle large image uploads, especially from mobile devices that capture high-resolution photos by default. Uploading these full-sized images directly to the backend or sending them to an AI or LLM (Large Language Model) for visual processing leads to predictable issues:</p>
<ul>
<li><p>Slow uploads</p>
</li>
<li><p>Overloaded backend systems</p>
</li>
<li><p>Increased LLM processing costs</p>
</li>
</ul>
<p>Most use cases, such as <strong>profile pictures, product thumbnails, or AI inputs</strong>, do not require such massive files. Using a 4K source only adds latency and cost without any real benefit.</p>
<h3 id="heading-the-solution-frontend-image-resizing"><strong>The Solution: Frontend Image Resizing</strong></h3>
<p>Frontend image resizing is a valuable technique that involves resizing and compressing images before they are sent to the backend. This approach can significantly reduce load times and cost without any noticeable drop in quality.</p>
<hr />
<h2 id="heading-how-it-works">How It Works</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762687821715/2cadfe15-fa7c-4473-806e-7d616f1812c2.png" alt class="image--center mx-auto" /></p>
<p>The typical workflow looks like this</p>
<ol>
<li><p>User selects an image.</p>
</li>
<li><p>The frontend resizes the image.</p>
</li>
<li><p>The optimized image is uploaded to the backend.</p>
</li>
<li><p>The backend sends it to an LLM or processing service.</p>
</li>
<li><p>The response is returned faster and at a lower processing cost.</p>
</li>
</ol>
<p>This optimization reduces the payload size, shortens upload duration, and minimizes downstream resource usage.</p>
<hr />
<h2 id="heading-approaches-to-resizing">Approaches to Resizing</h2>
<p>There are several options available, but here are the two main methods I recommend for resizing images within the browser:</p>
<h3 id="heading-1-canvas-native">1. Canvas (native)</h3>
<p>The browser’s <strong>Canvas 2D API</strong> lets you draw an image at a smaller size and export a new Blob/File. No dependencies, minimal code, very fast for typical uploads.</p>
<p><strong>When it shines</strong></p>
<ul>
<li><p>Screenshots, product photos, everyday camera images you’ll cap at <strong>~768–1080px</strong>.</p>
</li>
<li><p>You want <strong>no bundle impact</strong> and simple maintenance.</p>
</li>
</ul>
<p><strong>Implementation</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resizeWithCanvas</span>(<span class="hljs-params">file: File, maxSide = 768, quality = 0.9</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">File</span>&gt; </span>{
  <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">await</span> fileToImage(file);

  <span class="hljs-comment">// compute scaled dimensions keeping aspect ratio</span>
  <span class="hljs-keyword">const</span> scale = <span class="hljs-built_in">Math</span>.min(maxSide / img.width, maxSide / img.height);
  <span class="hljs-keyword">const</span> w = <span class="hljs-built_in">Math</span>.round(img.width * scale);
  <span class="hljs-keyword">const</span> h = <span class="hljs-built_in">Math</span>.round(img.height * scale);

  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'canvas'</span>);
  canvas.width = w; canvas.height = h;
  <span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">'2d'</span>)!;
  ctx.imageSmoothingEnabled = <span class="hljs-literal">true</span>;
  (ctx <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>).imageSmoothingQuality = <span class="hljs-string">'high'</span>;
  ctx.drawImage(img, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, w, h);

  <span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>&lt;Blob&gt;(<span class="hljs-function"><span class="hljs-params">r</span> =&gt;</span> canvas.toBlob(<span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> r(b!), <span class="hljs-string">'image/jpeg'</span>, quality));
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> File([blob], file.name.replace(<span class="hljs-regexp">/\.[^.]+$/</span>, <span class="hljs-string">'.jpg'</span>), { <span class="hljs-keyword">type</span>: <span class="hljs-string">'image/jpeg'</span> });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fileToImage</span>(<span class="hljs-params">file: File</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">HTMLImageElement</span>&gt; </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">res, rej</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> Image();
    img.onload = <span class="hljs-function">() =&gt;</span> res(img);
    img.onerror = rej;
    img.src = URL.createObjectURL(file);
  });
}
</code></pre>
<h3 id="heading-2-pica-library">2. Pica (library)</h3>
<p><strong>Pica</strong> is a high-quality re-sampler (using Lanczos3, with WASM/worker support). It's designed to provide better sharpness and fewer artifacts when significantly reducing the size of large images (e.g., 4000–6000px sources). It adds a small dependency, but the quality can be noticeably better on very large photos.</p>
<p><strong>When it shines</strong></p>
<ul>
<li><p>DSLR-scale inputs or highly detailed textures where Canvas starts to look slightly soft.</p>
</li>
<li><p>Optionally runs in a <strong>Web Worker</strong> to prevent main-thread blocking.</p>
</li>
</ul>
<p><strong>Implementation</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> pica <span class="hljs-keyword">from</span> <span class="hljs-string">'pica'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resizeWithPica</span>(<span class="hljs-params">file: File, maxSide = 768, quality = 0.9</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">File</span>&gt; </span>{
  <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">await</span> fileToImage(file);
  <span class="hljs-keyword">const</span> scale = <span class="hljs-built_in">Math</span>.min(maxSide / img.width, maxSide / img.height);
  <span class="hljs-keyword">const</span> w = <span class="hljs-built_in">Math</span>.round(img.width * scale);
  <span class="hljs-keyword">const</span> h = <span class="hljs-built_in">Math</span>.round(img.height * scale);

  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'canvas'</span>);
  canvas.width = w; canvas.height = h;

  <span class="hljs-keyword">const</span> p = pica();
  <span class="hljs-keyword">await</span> p.resize(img, canvas, { quality: <span class="hljs-number">3</span>, unsharpAmount: <span class="hljs-number">80</span>, unsharpRadius: <span class="hljs-number">0.6</span>, unsharpThreshold: <span class="hljs-number">2</span> });
  <span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">await</span> p.toBlob(canvas, <span class="hljs-string">'image/jpeg'</span>, quality);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> File([blob], file.name.replace(<span class="hljs-regexp">/\.[^.]+$/</span>, <span class="hljs-string">'.jpg'</span>), { <span class="hljs-keyword">type</span>: <span class="hljs-string">'image/jpeg'</span> });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fileToImage</span>(<span class="hljs-params">file: File</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">HTMLImageElement</span>&gt; </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">res, rej</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> Image();
    img.onload = <span class="hljs-function">() =&gt;</span> res(img);
    img.onerror = rej;
    img.src = URL.createObjectURL(file);
  });
}
</code></pre>
<h3 id="heading-comparison-overview">Comparison Overview</h3>
<p>Both approaches achieve the same goal: <strong>reduce image dimensions and file size before upload</strong>. The main difference lies in quality vs. simplicity trade-offs.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Criterion</td><td>Canvas (Native)</td><td>Pica (Library)</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Speed</strong></td><td>Very fast</td><td>Slightly slower</td></tr>
<tr>
<td><strong>Quality</strong></td><td>Excellent</td><td>Marginally sharper for 4K+ images</td></tr>
<tr>
<td><strong>Dependencies</strong></td><td>None</td><td>Requires library (~13.3 KB GZipped)</td></tr>
<tr>
<td><strong>Ease of Use</strong></td><td>Simple</td><td>Extra configuration</td></tr>
<tr>
<td><strong>Best For</strong></td><td>LLM, profile, and thumbnail uploads</td><td>High-detail photography apps</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-demo-canvas-vs-pica-in-practice">Demo: Canvas vs Pica in Practice</h2>
<p>Live Demo: <a target="_blank" href="http://web-image-resize.vercel.app">web-image-resize.vercel.app</a></p>
<p>Source Code: <a target="_blank" href="http://github.com/abdrahmansoltan/web-image-resize">github.com/abdrahmansoltan/web-image-resize</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762636314813/f3d0fa3f-7559-4f59-9397-9e716871ed1c.png" alt class="image--center mx-auto" /></p>
<p>The demo compares both resizing approaches side by side with these Key findings:</p>
<ul>
<li><p><strong>Canvas</strong> consistently reduced image size by up to <strong>~98%</strong> with minimal quality loss.</p>
</li>
<li><p><strong>Pica</strong> delivered slightly sharper edges for very large photos but required additional setup.</p>
</li>
<li><p>For images under 1080px, differences were nearly imperceptible.</p>
</li>
</ul>
<hr />
<h2 id="heading-findings-and-conclusion">Findings and Conclusion</h2>
<h3 id="heading-expected-impact">Expected Impact</h3>
<ul>
<li><p>Average image size: <strong>2.8 MB → 350 KB</strong></p>
</li>
<li><p>Upload duration: reduced by up to <strong>~98%</strong></p>
</li>
<li><p>Request time: reduced up to <strong>~60%</strong></p>
</li>
<li><p>Backend processing time: improved proportionally</p>
</li>
<li><p>LLM processing cost: reduced per pixel input</p>
</li>
<li><p>Image clarity: visually unchanged for standard use cases</p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Resizing images on the frontend is a simple yet powerful optimization that improves user experience and operational efficiency, especially for most applications, particularly those involving <strong>LLM inputs, profile pictures, or thumbnails.</strong></p>
<p><strong>Canvas API</strong> provides an ideal balance of performance, quality, and maintainability, as it’s native, fast, and introduce no dependencies. which is recommended for most cases.</p>
<p><strong>Pica</strong> remains a valuable alternative for applications handling extremely detailed images (e.g., photography tools or high-resolution artwork).</p>
<blockquote>
<p>Small optimization, significant results. Resize early, upload smart, and make every pixel count.</p>
</blockquote>
<h3 id="heading-references">References</h3>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage?utm_source=chatgpt.com">MDN: CanvasRenderingContext2D.drawImage()</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nodeca/pica?utm_source=chatgpt.com">Pica GitHub Repository</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Testing JavaScript Applications: Ch2. What to test and when?]]></title><description><![CDATA[In the previous article, I introduced a book called Testing Javascript Applications by Lucas da Costa and we discussed chapter 1 of the book which gives an Introduction to automated testing.
In this article, we will discuss chapter 2 of the book whic...]]></description><link>https://abdelrahmansoltan.hashnode.dev/testing-javascript-applications-ch2-what-to-test-and-when</link><guid isPermaLink="true">https://abdelrahmansoltan.hashnode.dev/testing-javascript-applications-ch2-what-to-test-and-when</guid><category><![CDATA[Jest]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Abdelrahman Soltan]]></dc:creator><pubDate>Mon, 10 Jul 2023 13:31:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687063018790/2f25cfbd-d2a8-4b98-9074-e072c922594f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://abdelrahmansoltan.hashnode.dev/testing-javascript-applications-ch1-introduction-to-automated-testing">the previous article</a>, I introduced a book called <a target="_blank" href="https://www.manning.com/books/testing-javascript-applications">Testing Javascript Applications</a> by <strong>Lucas da Costa</strong> and we discussed chapter 1 of the book which gives an Introduction to automated testing.</p>
<p>In this article, we will discuss chapter 2 of the book which discusses <strong>different types of automated tests and their advantages and disadvantages</strong>, providing guidance for selecting the appropriate tests to write.</p>
<hr />
<h2 id="heading-1-testing-pyramid">[1] Testing pyramid</h2>
<p>Developers use different types of tests for different purposes, frequencies, and durations. Tests can guide developers during development, test completed features, and interact with code or graphical interface (as an end-user would). The choice of selecting the right test type depends on the developer's judgment and the software's specific needs to build more reliable software. Understanding the types and purposes of tests is reflected in a fundamental concept in software testing: the <strong>testing pyramid</strong>.</p>
<p>The testing pyramid is a useful approach to ensure high-quality software development by showing the role, value, and frequency of tests. it involves 3 steps:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687234676543/57d6365b-aac9-4349-9e2a-aeea47b0cacb.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>The first step is to <strong>test individual units of code</strong>, just like testing individual functions, to ensure they work correctly. <strong><em>(Unit tests)</em></strong></p>
</li>
<li><p>The second step involves <strong>testing the integration of these units into larger components</strong>, like combining code modules to create a software feature. This validates that the different pieces of your software work together. <strong><em>(Integration tests)</em></strong></p>
</li>
<li><p>Finally, the last step is to test the final product, by verifying your work from a user’s perspective by interacting with your software through the user interface it provides. <strong><em>(E2E tests)</em></strong></p>
</li>
</ol>
<p>The size of each layer indicates the number of tests that should be written of that type, and the higher up a test is in the pyramid, the less frequently they run and the more value they provide.</p>
<p>By following this testing pyramid approach, we can ensure that our software meets high-quality standards and is useful to our users.</p>
<hr />
<h2 id="heading-2-testing-setup-and-frameworks">[2] Testing setup and frameworks</h2>
<p>To write a test any functionality, we can use manual tests for each condition (using <code>if..else</code>), or we can use a <strong>testing library</strong> because:</p>
<ul>
<li><p>It gets rid of the complicated logic to determine whether objects are equal to each other (using <code>if..else</code>), instead, it has built-in methods.</p>
</li>
<li><p>It generates meaningful testing output, so you don’t have to manipulate strings yourself.</p>
</li>
<li><p>It solves the problem of organizing and running multiple tests sequentially and in individual scopes, providing easily readable results, by using <strong>Test Runners</strong>.</p>
</li>
</ul>
<p>Currently, the most popular testing tool in the JavaScript ecosystem is <strong>Jest</strong>.</p>
<blockquote>
<p><a target="_blank" href="https://jestjs.io/">Jest</a> is a testing framework created at Facebook. It focuses on simplicity and, therefore, ships with everything, So you can start writing tests straightaway.</p>
</blockquote>
<p>To prepare tests for Jest, wrap them in the <code>test()</code> function that <strong>Jest</strong> adds to the global scope. This function helps organize multiple tests within a file and specifies which tests should run.</p>
<ul>
<li><p>The <code>test()</code> creates a block called "Test spec" and it's a function that takes the test's name as its first argument and a callback function that contains the actual test as the second argument.</p>
<pre><code class="lang-javascript">  test(<span class="hljs-string">'sample test'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Testing logic  </span>
  });
</code></pre>
</li>
<li><p>The <code>expect()</code> function helps <strong>Jest</strong> provide feedback that’s even more helpful.</p>
<pre><code class="lang-javascript">  test(<span class="hljs-string">'sample test'</span>, <span class="hljs-function">() =&gt;</span> {
    expect(<span class="hljs-number">1</span> + <span class="hljs-number">2</span>).toBe(<span class="hljs-number">3</span>); <span class="hljs-comment">// check if 1 + 2 = 3 using toBe() matcher utility</span>
  });
</code></pre>
<ul>
<li><p>It takes the actual subject of the assertion as an argument and returns an object that provides different <strong>matcher functions</strong>.</p>
<ul>
<li><p>These matcher functions verify whether the actual value matches your expectations.</p>
</li>
<li><p>they compare the value of the assertion’s target (the argument provided to <code>expect()</code>) to the value of the argument passed to them (<code>toBe()</code>)</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>Note for installing a testing library/framework:</strong></p>
<ul>
<li><p>Using a global installation of a testing library/framework can result in incorrect test results, indicating that the application is functioning correctly when it may not be.</p>
<blockquote>
<p>You want tests to fail only when there’s something wrong with your application, not when people are running different versions of a test framework.</p>
</blockquote>
</li>
<li><p>To avoid this, Install them as <strong>devDependencies</strong> using <code>"npm install [library] --save-dev"</code>. This ensures consistency for developers without shipping the library with the application.</p>
</li>
</ul>
<hr />
<h2 id="heading-3-unit-tests">[3] Unit tests</h2>
<p>Unit tests help you ensure that the smallest units of your software (your functions) behave as you expect them to.</p>
<ul>
<li><p>Unit tests’ scope is limited to a function, So their feedback is narrow and precise. They can immediately tell which function is failing. Strict feedback like this makes it faster to write and fix your code.</p>
</li>
<li><p>These tests are inexpensive and quick to write, but they cover only a small part of your application, and the guarantees they provide are weaker.</p>
</li>
<li><p>Just because functions work in isolation for a few cases doesn’t mean your whole software application works, too.</p>
<ul>
<li>So, to get the most out of these narrow and inexpensive tests, you should write many of them.</li>
</ul>
</li>
</ul>
<p>Considering that unit tests are <strong>numerous</strong> and <strong>inexpensive</strong>, and run quickly and frequently, we place these tests at the bottom of the testing pyramid. They’re the foundation other tests will build upon</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687668489843/40b8a735-911c-4c66-8033-2a2c8883fe84.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-31-writing-unit-tests">[3.1] Writing unit tests</h3>
<p>When writing a test, we follow this sequence known as <strong>"The three As"</strong>: (<strong><em>Arrange</em></strong>, <strong><em>Act</em></strong>, <strong><em>Assert</em></strong>).</p>
<ol>
<li><p>First, you set up a scenario -&gt; <strong>Arrange</strong></p>
</li>
<li><p>Then you call the function you want to test -&gt; <strong>Act</strong></p>
</li>
<li><p>And, finally, you check whether the output matches what you expected -&gt; <strong>Assert</strong></p>
</li>
</ol>
<p>Let's say that we want to test the behavior of a <strong>Shopping Cart</strong> and we want to test its functionalities in our app:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687240051118/8234f6e1-e9d6-4807-b6cb-9e39884d6b55.png" alt class="image--center mx-auto" /></p>
<p>we have a class <code>Cart</code> that has these functions:</p>
<ul>
<li><p><code>addItemToCart</code> functionality -&gt; <code>addToCart()</code> method</p>
</li>
<li><p><code>listCartItems()</code> functionality -&gt; <code>items</code> property</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Cart.js</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cart</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.items = [];
  }
  addToCart(item) {
    <span class="hljs-built_in">this</span>.items.push(item);
  }
}

<span class="hljs-built_in">module</span>.exports = Cart;
</code></pre>
<p>To test it, we create a <code>Cart.test.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Cart.test.js</span>
<span class="hljs-keyword">const</span> Cart = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./Cart.js"</span>);

test(<span class="hljs-string">"The addToCart function can add an item to the cart"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> cart = <span class="hljs-keyword">new</span> Cart(); <span class="hljs-comment">// 1. Arrange the test data</span>
  cart.addToCart(<span class="hljs-string">"cheesecake"</span>); <span class="hljs-comment">// 2. Act on the data</span>
  expect(cart.items).toEqual([<span class="hljs-string">"cheesecake"</span>]); <span class="hljs-comment">// 3. Assert the result</span>
});
</code></pre>
<p>As we can see, in unit tests, we only focus on testing small and isolated pieces of code.</p>
<p>Isolating your code in unit tests can be great for writing quick and simple tests, but unit tests can’t guarantee that you are using other pieces of software as you’re supposed to. That's where <strong>Integration tests</strong> come into place.</p>
<hr />
<h2 id="heading-4-integration-tests">[4] Integration tests</h2>
<p>Integration tests help you ensure that different components or modules of your software work together as expected. They verify that the interactions between these components are correct, data is transferred between modules properly, and the integrated system functions as intended.</p>
<ul>
<li><p>Integration testing is a critical step in the software development process as it helps to identify any defects or issues that arise when multiple components are combined and tested together.</p>
</li>
<li><p>By conducting integration tests, you can ensure that the different parts of your software work seamlessly together and deliver the desired functionality to end-users.</p>
</li>
</ul>
<p>The scope of these tests is broader than the scope of <strong>unit tests</strong> but smaller than the scope of <strong>end-to-end tests</strong>. They assert the quality of the intermediary steps of the process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687668457238/0d6788bc-9e70-48fa-8020-bfd522d32344.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-41-testing-3rd-party-libraries">[4.1] Testing 3rd party libraries</h3>
<p>If you are using a library like <a target="_blank" href="https://react.dev/">React</a>, for example, your software must integrate appropriately with it. The way React behaves is essential to how your application does, so you must test your code in integration with React.</p>
<ul>
<li><p>For example, you should not write tests for React's core built-in methods like <code>useState()</code> or <code>useEffect()</code>. Instead, you should write integration tests to ensure that your code correctly integrates with and utilizes these React methods in the intended way. By doing so, you can verify that your application functions as expected and takes full advantage of the features provided by React.</p>
</li>
<li><p>Testing the library methods is the author’s responsibility, not yours. And, if the authors didn’t write tests, it’s probably better to reconsider its adoption.</p>
</li>
</ul>
<p>The same is valid for interacting with a database or with a computer’s filesystem. You rely on how those external pieces of software work, and, therefore, it’s wise to check if you’re using them correctly.</p>
<blockquote>
<p>It’s important to highlight that the goal of an integration test is not to test any third-party pieces of software themselves. The purpose of an integration test is to check whether you are interacting with them correctly.</p>
</blockquote>
<p>When looking at the application’s infrastructure diagram, you will see that the scope of <strong>integration tests</strong> is broader than the scope of <strong>unit tests</strong>. They check how your functions interact and how your software integrates with third parties.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687623178407/057f0568-8dbd-4d24-8361-b3828019a6ca.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-42-example-a-test-that-talks-to-a-database">[4.2] Example: a test that talks to a database</h3>
<p>For example, we have a database that we're connected to, and will use it in our cart functionality:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// cart.js</span>
<span class="hljs-keyword">const</span> { db } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./dbConnection"</span>);

<span class="hljs-keyword">const</span> createCart = <span class="hljs-function"><span class="hljs-params">username</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> db(<span class="hljs-string">"carts"</span>).insert({ username });
};

<span class="hljs-built_in">module</span>.exports = {
  createCart
};
</code></pre>
<p>Writing tests for it will help ensure that your code works with the database correctly and that the APIs you’re using behave as you expect. So, If you had any incorrect queries, these tests would catch them.</p>
<p>To test the functions in the <code>cart.js</code> module, We can follow a pattern similar to the one we used in unit testing: (<strong>Arrange</strong>, <strong>Act</strong>, <strong>Assert</strong>)</p>
<p>Here's a test for <code>createCart()</code>. It should ensure that the database is clean, create a cart, and then check if the database contains the cart you’ve just created:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// cart.test.js</span>
<span class="hljs-keyword">const</span> { db, closeConnection } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./dbConnection"</span>);
<span class="hljs-keyword">const</span> { createCart } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./cart"</span>);

test(<span class="hljs-string">"createCart creates a cart for a username"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// 1. Arrange the test data</span>
  <span class="hljs-keyword">await</span> db(<span class="hljs-string">"carts"</span>).truncate(); <span class="hljs-comment">// Delete every row in the carts table</span>

  <span class="hljs-comment">// 2. Act on the data</span>
  <span class="hljs-keyword">await</span> createCart(<span class="hljs-string">"Lucas da Costa"</span>);

  <span class="hljs-comment">// Selects value in the username column for all the items in the carts table</span>
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.select(<span class="hljs-string">"username"</span>).from(<span class="hljs-string">"carts"</span>);

  <span class="hljs-comment">// 3. Assert the result</span>
  expect(result).toEqual([{ <span class="hljs-attr">username</span>: <span class="hljs-string">"Lucas da Costa"</span> }]);

  <span class="hljs-keyword">await</span> closeConnection(); <span class="hljs-comment">// Tears down the connection pool</span>
});
</code></pre>
<p>Usually in integration tests, we notice that we have prerequisites and postrequisites for tests that have dependencies like "connecting to a database", they can be handled in what we call <strong>"Testing Hooks"</strong>:</p>
<ul>
<li><p><code>afterAll</code> &amp; <code>afterEach</code> hooks to close the <strong>connection pool</strong> only after all tests have run and remove the invocation of <code>closeConnection()</code> from within the test.</p>
</li>
<li><p><code>beforeAll</code> &amp; <code>beforeEach</code> hooks to clean the database before they run. If you do this, there’s no need to repeat the <code>truncate()</code> statements on every test.</p>
</li>
</ul>
<p>So testing the interaction with the database using hooks will look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// cart.test.js</span>
<span class="hljs-keyword">const</span> { db, closeConnection } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./dbConnection'</span>);
<span class="hljs-keyword">const</span> { createCart, addItem } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./cart'</span>);

<span class="hljs-comment">// Prerequisites: clear the carts and carts_items tables before each test</span>
beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> db(<span class="hljs-string">'carts_items'</span>).truncate();
  <span class="hljs-keyword">await</span> db(<span class="hljs-string">'carts'</span>).truncate();
});

<span class="hljs-comment">// Postrequisites: tear down the connection pool once all tests have finished,</span>
afterAll(<span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> closeConnection());

test(<span class="hljs-string">'createCart creates a cart for a username'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> createCart(<span class="hljs-string">'Lucas da Costa'</span>);
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.select(<span class="hljs-string">'username'</span>).from(<span class="hljs-string">'carts'</span>);
  expect(result).toEqual([{ <span class="hljs-attr">username</span>: <span class="hljs-string">'Lucas da Costa'</span> }]);
});
</code></pre>
<hr />
<h2 id="heading-5-end-to-end-tests">[5] End-to-end tests</h2>
<p><strong>End-to-end tests</strong> are the most comprehensive tests, they validate your application by interacting with it as a real user would.</p>
<p>E2E tests don’t use your software’s code directly as unit tests do. Instead, they interact with it from an external perspective. So they end up covering a large surface of the application, as shown in the illustration. They rely on the client side working as well as all the pieces of software in the backend (the entire application).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687668854672/49be5496-747a-4bed-afc4-9f47f7d7f472.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>For example, In an E2E test to validate whether it’s possible to add an item to a cart:</p>
<ul>
<li><p>It wouldn’t directly call the <code>addToCart()</code> function.</p>
</li>
<li><p>Instead, it would open your web application, click the buttons with “Add to Cart” written on them, and then check the cart’s content by accessing the page that lists its items.</p>
</li>
</ul>
</li>
</ul>
<p>E2E tests also tend to take more time to run and, therefore, run less frequently. Unlike in unit tests, it’s not feasible to run end-to-end tests whenever you save a file.</p>
<p>E2E tests are very valuable and expensive, take relatively more time to run, and need a smaller quantity. So these tests are more suited for a later stage of the development process. Thus they are at the very top of the testing pyramid.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687669144888/8d41c46d-0fac-4872-a7f3-6432f4cd9937.png" alt class="image--center mx-auto" /></p>
<p>E2E tests can be divided into 2 parts:</p>
<ol>
<li><p>Testing HTTP APIs</p>
</li>
<li><p>Testing GUIs</p>
</li>
</ol>
<h3 id="heading-51-testing-http-apis">[5.1] Testing HTTP APIs</h3>
<p>Tests for <strong>HTTP APIs</strong> are excellent for ensuring that services follow the established <strong>“contracts”</strong>. When multiple teams have to develop different services, these services must have well-defined communication standards, which you can enforce through tests. Tests will help prevent services from not being able to talk to each other</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688993185805/16cbe2f1-c4c5-4353-90ee-7551bd938926.png" alt class="image--center mx-auto" /></p>
<p>HTTP API tests have a narrower scope than GUI tests as they only focus on probing the backend of the application. Hence, the testing pyramid divides the area for end-to-end tests and places HTTP API tests below GUI tests.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687670032593/c2c8ccf7-4192-4d25-bf59-c5736d1dad86.png" alt class="image--center mx-auto" /></p>
<p>For example, let's say we have an endpoint to <strong>get items in the cart</strong>:</p>
<ul>
<li><p>To retrieve the cart’s content, the client must send a request to <code>GET</code> <code>/carts/:username/items</code>.</p>
</li>
<li><p>To write a test that uses HTTP requests to add items to a cart and check the cart’s contents. Even though you are making HTTP requests instead of calling functions, the general formula for tests should be the same: (<strong>Arrange, Act, Assert</strong>)<strong>.</strong></p>
</li>
<li><blockquote>
<p>Because tests for RESTful APIs require only a client capable of performing HTTP requests and inspecting responses, we can write them within Jest using fetching libraries like <a target="_blank" href="https://github.com/axios/axios">Axios</a> to perform HTTP requests within our tests.</p>
</blockquote>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.test.js</span>
<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);
<span class="hljs-keyword">const</span> apiRoot = <span class="hljs-string">"http://localhost:3000"</span>;
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./server"</span>);

<span class="hljs-comment">// HELPER FUNCTIONS</span>
<span class="hljs-keyword">const</span> getItems = <span class="hljs-keyword">async</span> username =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`<span class="hljs-subst">${apiRoot}</span>/carts/<span class="hljs-subst">${username}</span>/items`</span>);
  <span class="hljs-keyword">return</span> response;
};

test(<span class="hljs-string">"getting cart items for a user"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// 1. Lists the items in a user’s cart</span>
  <span class="hljs-keyword">const</span> itemsResponse = <span class="hljs-keyword">await</span> getItems(<span class="hljs-string">"lucas"</span>);
  <span class="hljs-comment">// 2. Checks whether the response’s status is 200</span>
  expect(initialItemsResponse.status).toBe(<span class="hljs-number">200</span>);
  <span class="hljs-comment">// 3. Checks whether the response data include the correct items</span>
  expect(<span class="hljs-keyword">await</span> finalItemsResponse.data.json()).toEqual([<span class="hljs-string">"cake"</span>]);
});

<span class="hljs-comment">// Stops the server after finishing all tests</span>
afterAll(<span class="hljs-function">() =&gt;</span> app.close());
</code></pre>
<h3 id="heading-52-testing-guis">[5.2] Testing GUIs</h3>
<p>GUI <strong>(Graphical User Interface)</strong> tests cover your entire application. They will use its client-side to interact with your backend-side, therefore, touching every single piece of your stack. that's why they're usually called <strong>"UI tests".</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688993373468/041757f6-c474-42f9-ad49-7a7604751645.png" alt class="image--center mx-auto" /></p>
<p>UI tests require specialized tools capable of interacting with web page elements, such as buttons and forms, and controlling a real browser. Popular tools for UI testing, including <strong><em>Cypress</em></strong>, <strong><em>TestCafe</em></strong>, and <strong><em>Selenium</em></strong>, allow browser interaction through JavaScript control.</p>
<p>UI tests follow a similar structure to other types of tests, including setting up a scenario, performing actions, and making assertions. However, the main difference is that UI tests involve browser interactions, where actions happen through the browser, and assertions depend on a web page's content.</p>
<p>Because these tests cover all parts of your application, they have the highest place in the testing pyramid, as shown below. They take the longest to run, but they also provide the strongest possible guarantees.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687672836772/15cc123a-6374-4110-860b-c0c85ed89c15.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p><strong>The testing pyramid</strong> is a metaphor for categorizing tests based on their frequency, quantity, scope, and quality guarantees. As we move up the pyramid, tests become less frequent but more valuable, with broader scope and stronger quality guarantees.</p>
<p>Test runners organize tests, generate output, and include assertion libraries to compare the output. <strong>Jest</strong> is a popular example.</p>
<p>Tests follow a three-step formula: <strong>arrange</strong>, <strong>act</strong>, and <strong>assert</strong>, which involves setting up a scenario, triggering an action, and checking the results.</p>
<p><strong>Unit tests</strong> ensure software quality by testing functions at a granular level, providing precise feedback.</p>
<p><strong>Integration tests</strong> ensure that different parts of an application work together, and may require access to external third-party libraries or dependencies like databases.</p>
<p><strong>End-to-end tests</strong> interact with an application like a user, providing strong quality guarantees by simulating real-world use-case scenarios.</p>
<h1 id="heading-references"><strong>References</strong></h1>
<p><a target="_blank" href="https://www.manning.com/books/testing-javascript-applications">Testing Javascript Applications</a></p>
]]></content:encoded></item><item><title><![CDATA[Testing JavaScript Applications: Ch1. Introduction to automated testing]]></title><description><![CDATA[In this series, I'm going to discuss Testing Javascript Applications book by Lucas da Costa, This book focuses on automation testing from the perspective of software developers to build software more confidently, in less time and also to enable the Q...]]></description><link>https://abdelrahmansoltan.hashnode.dev/testing-javascript-applications-ch1-introduction-to-automated-testing</link><guid isPermaLink="true">https://abdelrahmansoltan.hashnode.dev/testing-javascript-applications-ch1-introduction-to-automated-testing</guid><category><![CDATA[Testing]]></category><category><![CDATA[automation testing ]]></category><category><![CDATA[Automated Testing]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[qa testing]]></category><dc:creator><![CDATA[Abdelrahman Soltan]]></dc:creator><pubDate>Sun, 11 Jun 2023 12:22:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686485595156/50d2a1fa-7c4e-41f1-9f98-02089b5bb943.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this series, I'm going to discuss <a target="_blank" href="https://www.manning.com/books/testing-javascript-applications">Testing Javascript Applications</a> book by <strong>Lucas da Costa</strong>, This book focuses on automation testing from the perspective of software developers to build software more confidently, in less time and also to enable the QA team to perform more creative and proactive work.</p>
<p>This book focuses on Putting those pieces together and answers questions like :</p>
<ul>
<li><p>Should software engineers always write tests?</p>
</li>
<li><p>If so, which types of tests, for what, and how many?</p>
</li>
<li><p>How do software development and QA fit together?</p>
</li>
</ul>
<p>This book contains 12 chapters divided into three main parts:</p>
<ol>
<li><p><strong>Testing JavaScript Applications</strong>: covers what automated tests are, why they are important, the different types of automated tests, and how each type of test impacts your projects.</p>
</li>
<li><p><strong>Writing Tests</strong>: uses practical examples to teach you how to write the different types of tests that you learned about in the first part.</p>
</li>
<li><p><strong>Business Impact</strong>: covers complementary techniques to amplify the positive impact that writing tests can have on your business.</p>
</li>
</ol>
<p>We will start with <strong>Chapter 1: Introduction to automated testing</strong> which explains what automated tests are and the advantages of writing them.</p>
<hr />
<h1 id="heading-1-what-is-an-automated-test">[1] What is an automated test?</h1>
<p>In the software industry, the demand for new features is growing fast, making it critical to frequently ship software that works. That’s what automated tests are here for, Developers focus on building features and no longer afford time for manual testing. At this point, writing tests is not only good practice, it’s an industry standard with more job postings requiring some degree of knowledge about automated software testing. Tests are valuable for projects of all sizes because they:</p>
<ul>
<li><p>Help avoid defects.</p>
</li>
<li><p>Reduce the cost of failure associated with a project.</p>
</li>
</ul>
<p>So, <strong>Automated testing</strong> is simply replacing customer interactions with code.</p>
<h3 id="heading-11-replacing-customer-interactions-with-code">[1.1] Replacing Customer Interactions with Code</h3>
<p>Let’s think about how a user tells our program to add something to a shopping cart:</p>
<ul>
<li><p>The application allows users to interact through a website that sends an <strong>HTTP</strong> request to the backend to add items to their cart. The <code>addToCart</code> function identifies the customer's cart based on the sender's session and updates the website according to the server's response.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686336930614/8fa83dd4-5e21-44db-88fd-5c368a146fa1.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>We can replace the customer with a piece of software that can call the <code>addToCart</code> function. Now, we don’t depend on someone to manually add items to a cart and look at the response. Instead, we have a piece of code that does the verification for us. <strong>That’s an automated test</strong>.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686337149386/441b9ac1-f3d1-42f9-8c5b-4396be650c7c.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Also, When testing, we can replicate user actions, such as adding items to a cart, to simulate real-world scenarios. By doing so, we can detect any potential bugs or issues before they occur in the production environment, which can be more costly to fix.</p>
</li>
</ul>
<h3 id="heading-12-testing-functionality-with-external-dependencies">[1.2] Testing Functionality with External Dependencies</h3>
<p>Tests can also interact with larger scopes by testing functionality that uses External Dependencies like a <strong>Database</strong>.</p>
<p>For example: if we want to test a scenario like <strong>"Adding a large number of macaroons to the cart"</strong>:</p>
<ul>
<li><p>Before that, The server must check the database to ensure that there are enough macaroons in stock before adding them to the cart. If the quantity requested is smaller than or equal to the amount in stock, the macaroons will be added to the cart, and the server will send a response to the client which updates accordingly.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686397783068/90219e0e-8781-4be8-9e40-e037164e5cdc.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<blockquote>
<p><strong>NOTE:</strong> When conducting tests, it is important to use a separate database instead of the production database to avoid the risk of losing data or causing inconsistencies. A separate database also allows for easier identification of bugs as the test environment is fully controlled and not influenced by customer actions.</p>
</blockquote>
<h3 id="heading-13-role-of-tests-in-software-development">[1.3] Role of Tests in Software Development</h3>
<p>Tests play a crucial role in software development, but it is important to understand their limitations. <strong>"Tests can’t prove your software works; they can only prove it doesn’t"</strong>.</p>
<p>Tests serve as <strong>experiments</strong>, encoding the developer's expectations about how the software should behave. However, just because tests have passed in the past does not always mean the application will behave the same way in the future. That's why it's important to increase the efficacy of tests to ensure that they closely resemble what real users do. The more tests there are, and the more they simulate actual user behavior, the more guarantees they provide.</p>
<p>Despite all this, <strong>automated tests do not replace the need for manual testing</strong>. Manual testing, which involves verifying work as end-users would do, and investing time in exploratory testing, is still indispensable.</p>
<hr />
<h1 id="heading-2-why-automated-tests-matter">[2] Why automated tests matter?</h1>
<p>Tests provide quick and fail-proof feedback, which improves the development process in several ways. We will examine how <strong>swift and precise feedback</strong> can:</p>
<ol>
<li><p>Makes the development workflow more uniform and predictable</p>
</li>
<li><p>Facilitates issue reproduction and test case documentation</p>
</li>
<li><p>Enhances collaboration among different teams</p>
</li>
<li><p>Shortens the time to deliver high-quality software</p>
</li>
</ol>
<h3 id="heading-21-predictability">[2.1] Predictability</h3>
<p>Having a predictable development process means preventing the introduction of unexpected behavior during the implementation of a feature or the fixing of a bug. Also Reducing the number of surprises during development makes tasks easier to estimate and causes developers to revisit their work less often.</p>
<ul>
<li><p>Manually ensuring that your entire software works as you expect is a time-consuming and error-prone process.</p>
</li>
<li><p>Tests improve this process because they decrease the time it takes to get feedback on the code you write and, therefore, make it quicker to fix mistakes.</p>
</li>
</ul>
<p>To illustrate how tests can make development more predictable, let’s imagine we have a new feature: "<strong>Enable customers to track the status of their orders"</strong></p>
<ul>
<li><p>Manually testing the "tracking" feature of the shopping process would be time-consuming, requiring the tester to go through the <strong>(testing environment setup + shopping process)</strong> each time. This would involve:</p>
<ol>
<li><p>clearing databases</p>
</li>
<li><p>opening a browser</p>
</li>
<li><p>adding items to the cart</p>
</li>
<li><p>scheduling a delivery, going through the checkout</p>
</li>
<li><p>finally testing the tracking feature.</p>
</li>
</ol>
</li>
</ul>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686410313271/9e429c9f-bdb9-401d-97a4-1d248e4526e3.png" alt class="image--center mx-auto" /></p>
<ul>
<li>As we can see, the <strong>manual testing process</strong> is time-consuming, developers may write bigger chunks of code at a time, delaying feedback on the software's performance, and By the time the feedback arrives, it may be too late, making it difficult to locate bugs in the new code written before the testing process.</li>
</ul>
<ul>
<li><p>With an <strong>automated test</strong>, you can write less code before getting feedback. So your automated tests can call the <code>trackOrder</code> functionality directly and you can avoid touching unnecessary parts of your application before you’re sure that the <code>trackOrder</code> functionality works correctly.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686411355532/3c9a6e2f-8c1f-4797-9bcb-4f2fac5f81e8.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p>Writing tests for small chunks of code can make it easier to locate bugs and issues. If a test fails after writing only a few lines of code, it is easier to identify the root cause of the issue. on the other hand, if a large amount of code is written before testing, it becomes more challenging to identify the source of the problem, especially if other parts of the application are affected.</p>
<ul>
<li>Automated tests provide early detection of issues, enabling developers to make corrections quickly. Running tests frequently provides precise feedback on which part of the application is broken as soon as it occurs.</li>
</ul>
<blockquote>
<p>T<strong>he less time it takes to get feedback once you’ve written code, the more predictable your development process will be</strong>.</p>
</blockquote>
<h3 id="heading-22-reproducibility">[2.2] Reproducibility</h3>
<p>In manual testing, <strong>the more steps a particular task has, the more likely a human is to make mistakes following them</strong>. This process is also long and error-prone, and there are many different ways to approach each testing step.</p>
<p>Automated tests ensure that these steps are followed to the letter, making it easier and quicker to <strong>reproduce bugs</strong> to fix them and ensure they aren’t present anymore. This is particularly important when testing applications that have multiple testing steps because we can ensure that these steps are followed exactly each time.</p>
<p>Also, If the list of test cases grows too long or if there are too many steps, the room for human mistakes gets bigger.</p>
<ul>
<li><p>Here is an example of a case with multiple bugs that require the same <strong>repeated steps</strong> to be reproduced and <strong>tested manually</strong>:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686473395204/47236c20-a041-4ff8-bc64-904da7f669a4.png" alt /></p>
</li>
<li><p>Even if you decide to maintain a checklist for those test cases, you will have the overhead of keeping that documentation always up-to-date. If you ever forget to update it and something not described in a test case happens, who’s wrong—the application or the documentation?</p>
</li>
</ul>
<blockquote>
<p>Automated tests do the exact same actions every time you execute them. When a machine is running tests, it neither forgets any steps nor makes mistakes.</p>
</blockquote>
<h3 id="heading-23-collaboration">[2.3] Collaboration</h3>
<p>When working with other developers, new concerns arise regarding how changes to different parts of the application may interfere with each other.</p>
<p>For example, if you are working on a new feature <code>feat 1</code> and another developer (Alice) is working on another feature <code>feat 2</code>, How to ensure that the 2 features are not going to interfere with each other?</p>
<ul>
<li><p>This requires coordination and communication to ensure that everyone's work is compatible and doesn't interfere with each other. This is done by manually testing changes that are resulted from the <strong>integration</strong> between both features, and this will be additional effort added for testing.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686474483382/5285779e-556d-418a-b9bb-7747ae68a3ff.png" alt /></p>
</li>
<li><p>Besides being time-consuming, this process is also error-prone. You have to remember all the steps and edge cases to test in both your work and Alice’s, an also still need to follow them exactly.</p>
</li>
</ul>
<p>Adding automated tests to solve this benefits everyone. Tests serve as <strong>up-to-date documentation</strong>, guiding further work and saving time in the future. If multiple developers are working on a project, automated tests ensure that changes to one part of the application don't interfere with another.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686475042182/85e90228-f77e-4b6b-b72c-05bacea4f30d.png" alt class="image--center mx-auto" /></p>
<p>Writing tests whenever changes are made creates a productive collaboration cycle, where one developer helps those who will work on that part of the codebase next.</p>
<blockquote>
<p>Well-written tests are the best documentation a developer can have because they are always up-to-date and need to pass.</p>
</blockquote>
<p>This approach reduces communication overhead but does not eliminate the need for communication, which is the foundation stone for every project to succeed. Automated tests remarkably improve the collaboration process, but they become even more effective when paired with other practices, such as <strong>code reviews</strong>.</p>
<h3 id="heading-24-speed">[2.4] Speed</h3>
<p>While tests improve the development process, ultimately, what matters for the business is <strong>speed</strong> and <strong>correctness</strong>. When developers can produce code quickly, ensure it's bug-free, and integrate it with everyone else's work, the business succeeds. Preventing regressions and making deployments safer, and also contribute to the success of the business.</p>
<p>While writing tests does have a cost in terms of time, the benefits are greater than the drawbacks. Although writing a test may take more time initially than doing a manual test, the value extracted from it increases the more it's run.</p>
<p>For example:</p>
<ul>
<li><p>If it takes five minutes to write an automated test and one minute to do a manual test, as soon as the automated test runs for the fifth time, it will have paid for itself. Given how frequently tests are run, the time invested in writing them is well worth it.</p>
</li>
<li><p>In contrast to manual testing, which will always take the same amount of time or more. <strong>As time passes, the total effort involved in manual tests grows much quicker</strong>.</p>
</li>
</ul>
<p>The difference in effort and time between writing automated tests and performing manual testing:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686476650194/3ce60041-21a2-4574-8f43-eab95f9d46cc.png" alt class="image--center mx-auto" /></p>
<p>Writing tests is like making an investment in stocks. You may pay a big price up-front, but you will continue to reap the payoffs for a long time.</p>
<ul>
<li><p>Long-term projects benefit the most from tests because the longer the project runs, the more effort is saved, and the more you can invest in new features or other meaningful activities.</p>
</li>
<li><p>Short-term projects don't benefit much from tests since they don't live long enough to justify the effort saved with testing over time.</p>
</li>
</ul>
<hr />
<h1 id="heading-summary">Summary</h1>
<p>Automated tests are programs that automate the task of testing software. They interact with the application, compare its output to expected output, and provide feedback when output is incorrect.</p>
<p>Automated tests reduce the distance between writing code and receiving feedback, making the development process more predictable and reducing surprises. They always follow the same steps and make it easier to reproduce bugs. Well-written tests serve as documentation for developers and help businesses deliver higher-quality software faster, driving up profits.</p>
<p>Although writing tests requires an investment of time, the more often a test runs, the more time it saves, making them more critical for long-term projects.</p>
<h1 id="heading-references"><strong>References</strong></h1>
<p><a target="_blank" href="https://www.manning.com/books/testing-javascript-applications">Testing Javascript Applications</a></p>
]]></content:encoded></item><item><title><![CDATA[The Secret Weapon for Frontend Developers: Data Structures and Algorithms]]></title><description><![CDATA[As a frontend developer, learning data structures and algorithms may not seem like a priority. After all, your job is to build user interfaces, not write complex algorithms or manipulate data structures. While building user-friendly interfaces is imp...]]></description><link>https://abdelrahmansoltan.hashnode.dev/the-secret-weapon-for-frontend-developers-data-structures-and-algorithms</link><guid isPermaLink="true">https://abdelrahmansoltan.hashnode.dev/the-secret-weapon-for-frontend-developers-data-structures-and-algorithms</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[frontend]]></category><category><![CDATA[data structures]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[Abdelrahman Soltan]]></dc:creator><pubDate>Wed, 24 May 2023 06:23:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684902560131/53dc725f-16fb-46e2-aab3-dc27c2f055ff.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a frontend developer, learning data structures and algorithms may not seem like a priority. After all, your job is to build user interfaces, not write complex algorithms or manipulate data structures. While building user-friendly interfaces is important, optimizing performance and efficiency is equally crucial. That's why learning about data structures and algorithms can provide several benefits that can help you build better, more efficient code.</p>
<p>In this article, we'll explore why data structures and algorithms matter for frontend development, provide real-life examples of how they have been used in frontend projects, and show you how to leverage them to take your frontend skills to the next level. Whether you're just starting out or a seasoned pro, learning about data structures and algorithms is a must if you want to stay ahead of the curve in this rapidly changing industry.</p>
<hr />
<h2 id="heading-1-what-are-data-structures-and-algorithms">[1] What are Data structures and algorithms?</h2>
<p><img src="https://www.educative.io/api/page/5717848287608832/image/download/6604705313587200" alt="editor-page-cover" class="image--center mx-auto" /></p>
<p>Data structures and algorithms <strong>(D&amp;A)</strong> are two of the most fundamental concepts in computer science. Data structures are ways of organizing data, while algorithms are procedures for solving problems. Together, they form the basis of all computer programming.</p>
<ul>
<li><p><strong>Data structures</strong> allow us to store and organize data in a way that is efficient and easy to access. For example, if we are building a website that displays a list of products, we need to store the product data in a data structure that allows us to quickly find the product that the user is looking for.</p>
</li>
<li><p><strong>Algorithms</strong> allow us to solve problems that are too complex to be solved manually. For example, if we are building a website that allows users to search for products, we need to use an algorithm to search the product database and return the results to the user.</p>
</li>
</ul>
<p>There are many different types of data structures and algorithms, and each one has its strengths and weaknesses. The best data structure or algorithm for a particular problem will depend on the specific requirements of the problem.</p>
<hr />
<h2 id="heading-2-how-data-structures-and-algorithms-can-be-used-in-frontend-development">[2] How data structures and algorithms can be used in frontend development?</h2>
<p><img src="https://res.cloudinary.com/proxify-io/image/upload/f_auto,dpr_auto,c_fill,q_auto:best,w_1728,h_720/v1/cms/images/seo-pages/9ipeYTUaXBUMYp7rBWw9ftFmTr8EZdclnDZVbek7.png" alt="Hire quality freelancers at affordable rates | Proxify.io" class="image--center mx-auto" /></p>
<p>As a frontend developer, you may be used to focusing on creating visually appealing interfaces and writing clean, maintainable code. However, there's another aspect of coding that can significantly impact the performance and efficiency of your applications: Data structures and Algorithms. By understanding some concepts, you can optimize your code, solve complex problems, improve user experience, and future-proof your skills. Here are some of the key benefits:</p>
<h3 id="heading-21-optimizing-performance"><strong>[2.1] Optimizing Performance</strong></h3>
<p>In frontend development, performance is critical. Slow-loading pages or clunky interfaces can turn users away and hurt your website's effectiveness. By using the right data structure or algorithm, you can optimize your code for performance. For example:</p>
<ul>
<li><p>Using arrays instead of loops to search for data can significantly speed up your code's execution time.</p>
<ul>
<li>Let's say you have a large dataset that you need to search through frequently. Using a linear search algorithm to find a specific item could be slow and inefficient, especially if the dataset is very large. However, by using a <strong>binary search algorithm</strong> with a sorted array, you can significantly improve performance. This is because a binary search algorithm can find the target item in a much shorter amount of time by dividing the dataset in half at each iteration.</li>
</ul>
</li>
<li><p>Another example is when you need to render a large list of items on a webpage. Using a simple loop to iterate through the list and render each item can be slow and cause the page to lag. However, by using a technique called <strong>virtualization</strong>, you can render only the items that are currently visible on the screen, and dynamically load additional items as needed. This can significantly improve the performance and speed of your application.</p>
</li>
</ul>
<h3 id="heading-22-improving-user-experience"><strong>[2.2]</strong> Improving User Experience</h3>
<p>Good user experience is key to retaining users and keeping them engaged with your website. By using data structures and algorithms, you can build more interactive and dynamic user interfaces. For example:</p>
<ul>
<li><p>Let's say you have a form with multiple input fields, and you want to highlight the next field when the user finishes filling out the current field. By using a queue data structure, you can keep track of the order in which the fields should be filled out and easily highlight the next field when the user submits the current one. This can help users navigate the form more easily and provide a better overall experience.</p>
</li>
<li><p>Another example is when you want to implement autocomplete functionality in a search bar. By using a <strong>Trie</strong> data structure, you can efficiently store and search through a large dictionary of possible search terms. This can provide users with more accurate and relevant search suggestions, and make the search experience faster and more seamless.</p>
</li>
</ul>
<h3 id="heading-23-solving-complex-problems"><strong>[2.3]</strong> Solving Complex Problems</h3>
<p>Frontend development often involves solving complex problems, such as <strong>data processing</strong>, <strong>caching</strong>, and <strong>real-time updates</strong>. By using the appropriate data structures and algorithms, you can tackle these challenges more effectively. For example:</p>
<ul>
<li><p>Let's say you're building a real-time chat application that needs to display messages in chronological order as they are received. By using a <strong>heap</strong> data structure to store the messages, you can keep them in the correct order based on their timestamp, and easily retrieve the most recent messages to display to the user. This can make the chat application more efficient and responsive, and provide a better user experience.</p>
</li>
<li><p>Another example is when you need to implement caching in <strong>localstorage</strong> to improve performance. By using a cache data structure, such as a <strong>hash table</strong> or a <strong>Least Recently Used (LRU</strong>) you can store frequently accessed data in memory for faster access. This can significantly improve the speed and efficiency of your application, especially when working with large datasets or repeated expensive operations.</p>
</li>
</ul>
<h3 id="heading-24-understanding-the-browser"><strong>[2.4] Understanding the Browser</strong></h3>
<p>Understanding data structures and algorithms can also help you understand how the browser works behind the scenes, which can be valuable for debugging and troubleshooting. For example:</p>
<ul>
<li>Let's say you're experiencing performance issues with your website, and you suspect that it may be related to the way the browser is handling the <strong>Document Object Model (DOM) tree</strong>. By understanding how the browser parses and manipulates the <strong>DOM tree</strong>, you can more easily identify the root cause of the performance issue and optimize your code accordingly. Additionally, understanding the browser's caching and rendering mechanisms can help you create more efficient and faster applications.</li>
</ul>
<h3 id="heading-25-future-proofing-your-skills"><strong>[2.5] Future-Proofing Your Skills</strong></h3>
<ul>
<li><p>Finally, learning about data structures and algorithms can future-proof your skills as a frontend developer. As technology continues to evolve, the ability to understand and apply these concepts will become increasingly valuable. For example:</p>
<ul>
<li>Many modern frontend frameworks, such as <strong>React</strong> and <strong>Vue.js</strong>, rely heavily on data structures and algorithms to optimize performance and provide a seamless user experience. By understanding these concepts, you can more easily understand how these frameworks work under the hood, and how to use them effectively to build scalable and efficient applications.</li>
</ul>
</li>
<li><p>Additionally, as the field of frontend development continues to grow and change rapidly, it's important to stay up-to-date with the latest trends and technologies. By investing time in learning about data structures and algorithms, you can develop a deeper understanding of how different frontend technologies work, and how to choose the best tools and frameworks for your projects. This can help you stay ahead of the curve and remain competitive in this fast-paced industry.</p>
</li>
</ul>
<p>These are just a few examples of how learning data structures and algorithms can benefit frontend developers. If you are a frontend developer, I encourage you to learn these essential concepts. It is an investment that will pay off in the long run.</p>
<hr />
<h2 id="heading-3-real-life-examples-of-how-data-structures-and-algorithms-have-been-used-in-frontend-development">[3] Real-life examples of how data structures and algorithms have been used in frontend development</h2>
<p><img src="https://assets-global.website-files.com/6253f6e60f27498e7d4a1e46/627abacf01bff344c120c07b_How-to-build-the-perfect-frontend-web-dev-portfolio.jpeg" alt="What is a front end developer? Skills to become a front end web developer" class="image--center mx-auto" /></p>
<p>Now that we've explored the benefits of data structures and algorithms for frontend development, let's take a look at some real-life examples of how these concepts have been applied in actual projects.</p>
<blockquote>
<p><strong>Note:</strong> Most of these examples will happen if we're storing data from frontend side and not dealing with database from the backend. However, data structures and algorithms are still important for backend developers, as they can help to improve the performance and efficiency of their code.</p>
</blockquote>
<ol>
<li><p><strong>Autocomplete search bars</strong></p>
<ul>
<li><strong>Tries</strong> can be used to efficiently store and search through a large dictionary of possible search terms for autocomplete functionality.</li>
</ul>
</li>
<li><p><strong>Chat applications</strong></p>
<ul>
<li><strong>Heaps</strong> can be used to store messages and keep them ordered by their timestamps so that the most recent messages can be easily retrieved and displayed.</li>
</ul>
</li>
<li><p><strong>Rendering lists with a large number of records</strong></p>
<ul>
<li><strong>Virtualization</strong> techniques can be used to render only the items that are currently visible on the screen, and dynamically load additional items as needed, improving the performance and speed of the application.</li>
</ul>
</li>
<li><p><strong>Real-time data processing</strong></p>
<ul>
<li>Data structures and algorithms can be used to choose the most efficient and scalable approach to processing data, such as using a <strong>radix tree</strong> for <strong>IP address lookups</strong> to improve speed and scalability.</li>
</ul>
</li>
<li><p><strong>Forms with multiple input fields</strong></p>
<ul>
<li><strong>Queues</strong> can be used to keep track of the order in which the fields should be filled out and easily highlight the next field when the user submits the current one.</li>
</ul>
</li>
<li><p><strong>Handling application state</strong></p>
<ul>
<li>Understanding different data structures can help frontend developers choose the right data structure for a particular task, such as using a <strong>hash table</strong> instead of an <strong>array</strong> to store products in a shopping cart and other different state items.</li>
</ul>
</li>
<li><p><strong>Filtering data</strong></p>
<ul>
<li><strong>Binary search trees (BST)</strong> can be used to quickly find data items that match specific criteria when displaying a list of data on a frontend.</li>
</ul>
</li>
<li><p><strong>Recursion &amp; Call Stack</strong></p>
<ul>
<li><p>The browser <strong>call stack</strong> is the closest thing to the <strong>stack</strong> that is used by frontend developers. The browser <strong>call stack</strong> is a data structure that stores information about the functions that are currently executing in a web browser. The call stack is used to track the execution of functions and to help debug JavaScript code.</p>
</li>
<li><p>The <strong>call stack</strong> can be used to debug JavaScript code by providing information about the current function that is executing and the functions that have been called before it. This information can be used to identify the source of a JavaScript error.</p>
</li>
</ul>
</li>
<li><p><strong>Frontend Libraries</strong></p>
<ol>
<li><p><strong>React Virtualized</strong> uses windowing and a combination of data structures and algorithms to efficiently manage large, scrollable lists and tables in React applications.</p>
</li>
<li><p><strong>D3.js</strong> provides a wide range of data structures and algorithms for manipulating and visualizing data, such as force-directed graph layouts that use physics simulations to position nodes.</p>
</li>
<li><p><strong>Lodash</strong> includes various data manipulation and functional programming tools, such as memoization using hash table data structures to cache function call results.</p>
</li>
</ol>
</li>
</ol>
<ul>
<li><blockquote>
<p>By using libraries like these, frontend developers can leverage the power and efficiency of data structures and algorithms to build faster, more responsive, and more scalable applications.</p>
</blockquote>
</li>
</ul>
<p>These are just a few examples of how data structures and algorithms can be used in frontend development. By utilizing these tools effectively, developers can create faster, more efficient, and more user-friendly applications.</p>
<hr />
<h2 id="heading-4-how-to-learn-data-structures-and-algorithms-as-a-frontend-developer">[4] How to Learn Data Structures and Algorithms as a Frontend Developer</h2>
<h3 id="heading-41-choosing-the-right-programming-language-for-learning-dandampa">[4.1] Choosing the right programming language for learning D&amp;A</h3>
<p>Learning data structures and algorithms is important for frontend developers, However, it's worth noting that <strong>JavaScript</strong>, the primary language used in frontend development, doesn't have built-in support for most data structures and algorithms, so developers need to create them from scratch or use libraries and modules that provide these functionalities.</p>
<p>Fortunately, there are many resources available to help frontend developers learn and apply these concepts effectively. For example, developers can use other languages like <strong>Python</strong>, <strong>C++</strong>, or <strong>Java</strong>, which have built-in support for many data structures and algorithms. Additionally, there are many libraries and modules available for <strong>JavaScript</strong> that provide similar functionalities. By leveraging these resources, frontend developers can build more efficient and effective applications that meet user needs.</p>
<h3 id="heading-42-tips-for-learning-data-structures-and-algorithms">[4.2] Tips for learning data structures and algorithms</h3>
<ul>
<li><p><strong>Start with the basics</strong></p>
<ul>
<li>Begin by learning fundamental concepts such as <strong>arrays</strong>, <strong>linked lists</strong>, <strong>stacks</strong>, <strong>queues</strong>, <strong>sorting algorithms</strong>, and <strong>searching algorithms</strong>. Understanding these concepts will help you grasp more advanced topics.</li>
</ul>
</li>
<li><p><strong>Practice implementation</strong></p>
<ul>
<li><p>Implement data structures and algorithms in your code, starting with simple examples and gradually working your way up to more complex problems. This approach will help you gain hands-on experience and build your skills.</p>
</li>
<li><p>Websites like <a target="_blank" href="https://leetcode.com/">LeetCode</a>, <a target="_blank" href="https://www.hackerrank.com/">HackerRank</a>, and <a target="_blank" href="https://codeforces.com/">Codeforces</a> offer exercises and problems to practice implementing these concepts.</p>
</li>
</ul>
</li>
<li><p><strong>Utilize online resources</strong></p>
<ul>
<li><p>There are many free and paid resources available online, including courses, tutorials, and books that cover data structures and algorithms.</p>
</li>
<li><p>My recommendation is to go with "<strong>Data Structures and Algorithms in Python</strong>" by <em>Michael T. Goodrich, Roberto Tamassia, and Michael H. Goldwasser</em>.</p>
</li>
</ul>
</li>
<li><p><strong>Learn from other developers</strong></p>
<ul>
<li>Collaborate with experienced developers through online communities, meetups, and mentorship programs. This approach can help you learn new techniques, get feedback, and gain a better understanding of how these concepts are applied in real-world scenarios.</li>
</ul>
</li>
<li><p><strong>Apply what you learn</strong></p>
<ul>
<li>Look for opportunities to use data structures and algorithms in your frontend development projects to optimize performance, improve user experience, and solve complex problems. By applying what you learn, you can gain a deeper understanding of these concepts and develop the skills necessary to become a more efficient and effective frontend developer.</li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-conclusion">conclusion</h2>
<p>In conclusion, data structures and algorithms are essential. By learning about data structures and algorithms, frontend developers can use them to build websites that are more efficient, high-performing, and user-friendly.</p>
<p>There are many resources available to learn about data structures and algorithms, including books, online courses, and tutorials. I encourage you to check out some of these resources and start learning about data structures and algorithms today.</p>
<p>By investing time in learning these concepts and applying them to your work, you can build better, more efficient code and stay ahead of the curve in this rapidly changing industry.</p>
]]></content:encoded></item></channel></rss>