<?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[Kelvin Liang's Blog]]></title><description><![CDATA[I am a self-taught coder from the world of sysAdmin, I like to surrounded by computers, gigs, great ideas and families. 
I was committed to #publichLearning 50 days ago and I will continue.]]></description><link>https://blog.kelvinliang.cn</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 23:02:36 GMT</lastBuildDate><atom:link href="https://blog.kelvinliang.cn/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to write tests for React in 2020 - part 2]]></title><description><![CDATA[Writing React Test with React recommend libraries - Jest & Testing Library for React Intermediate users.

Please Note
In this article, I will explore more advanced concepts in React Testing, I hope you find them helpful for your situations. If you ar...]]></description><link>https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-2</link><guid isPermaLink="true">https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-2</guid><category><![CDATA[React]]></category><category><![CDATA[Testing]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Facebook]]></category><category><![CDATA[Jest]]></category><dc:creator><![CDATA[Kelvin Liang]]></dc:creator><pubDate>Thu, 18 Jun 2020 10:14:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1592206628433/sJYuUdo-f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Writing React Test with React recommend libraries - Jest &amp; Testing Library for React Intermediate users.</p>
</blockquote>
<h1 id="please-note">Please Note</h1>
<p><em>In this article, I will explore more advanced concepts in React Testing, I hope you find them helpful for your situations. If you are a beginner in React or new to testing, I would suggest you check out <a target='_blank' rel='noopener noreferrer'  href="https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-1-ckawd0emy07rvbbs1p02tayit">Part 1 Here</a> to have some fundamental knowledge before continue, thanks!</em></p>
<h2 id="first-let-s-look-at-the-accessibility-test-">First, let&#39;s look at the <strong>Accessibility Test</strong>.</h2>
<p>Front-end development is all about visualization and interacting with end-users, <strong>Accessibility Test</strong> can ensure our apps can reach as many as possible users out there. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592211900991/5nY_dRbeH.png" alt=" From - https://reactjs.org/docs/accessibility.html ">
<strong>From - <a target='_blank' rel='noopener noreferrer'  href="https://reactjs.org/docs/accessibility.html">https://reactjs.org/docs/accessibility.html</a></strong></p>
<p>Writing <strong>Accessibility Test</strong> for every aspect of your app seems very intimidated, but thanks for <strong><a target='_blank' rel='noopener noreferrer'  href="https://www.deque.com/company/">Deque Systems</a></strong> -  A company dedicated on improving software accessibility by offering <strong><a target='_blank' rel='noopener noreferrer'  href="www.deque.com/axe/">Axe</a></strong> testing package freely available online, we can now easily leverage the expertise from many senior developers around the world by importing <a target='_blank' rel='noopener noreferrer'  href="https://www.npmjs.com/package/jest-axe">Jest-axe</a> along with Jest Library to test a web app&#39;s accessibility. </p>
<pre><code class="lang-bash">npm install --save-dev jest-axe
</code></pre>
<p>or </p>
<pre><code class="lang-bash">yarn add --dev jest-axe
</code></pre>
<p>With the package install, we can add the<strong> Accessibility Test</strong> into a project like this:</p>
<pre><code class="lang-Javascript"><span class="hljs-comment">// App.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> { axe, toHaveNoViolations } <span class="hljs-keyword">from</span> <span class="hljs-string">'jest-axe'</span>;
expect.extend(toHaveNoViolations);

describe(<span class="hljs-string">'App'</span>, () =&gt; {
  test(<span class="hljs-string">'should have no accessiblity violations'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { container } = render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>)</span>;
    <span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> axe(container);
    expect(results).toHaveNoViolations();
  });
});
</code></pre>
<p>It will help to ensure your FrontEnd Development complies with the latest version of <a target='_blank' rel='noopener noreferrer'  href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG(Web Content Accessibility Guidelines)</a>. For example, if you assign a wrong role to your Navigation bar component,</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./components/navBar.js</span>
...
&lt;div className=<span class="hljs-string">"navbar"</span> role=<span class="hljs-string">'nav'</span>&gt;
   ...
&lt;<span class="hljs-regexp">/div&gt;
...</span>
</code></pre>
<p>It will alert you like below: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592231470736/ApsO2Ms4L.png" alt="a11y-test-wrong-role.png"></p>
<blockquote>
<p>Check out a List of WAI-ARIA Roles - <a target='_blank' rel='noopener noreferrer'  href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles">Here</a>.</p>
</blockquote>
<p>Replace <code>nav</code> with <code>navigation</code> role as below, the test will pass. </p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./components/navBar.js</span>
...
&lt;div className=<span class="hljs-string">"navbar"</span> role=<span class="hljs-string">'navigation'</span>&gt;
   ...
&lt;<span class="hljs-regexp">/div&gt;
...</span>
</code></pre>
<p>As we can see above, this test will help ensure you follow the <a target='_blank' rel='noopener noreferrer'  href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG(Web Content Accessibility Guidelines)</a> standard so your app can reach most of the people out there.</p>
<h2 id="second-adding-a-snapshot-test-">Second, adding a <strong>Snapshot Test</strong>.</h2>
<blockquote>
<p>Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly. --  From <a target='_blank' rel='noopener noreferrer'  href="http://jestjs.io/">Jest</a></p>
</blockquote>
<p><strong>You can put the test on the entire app or one specific component.</strong></p>
<p><em>They can serve different purposes during the development cycle, you can either use <strong>Snapshot Test</strong> to ensure your app&#39;s UI doesn&#39;t change over time or compare the differences between the last snapshot with current output to iterate through your development.</em></p>
<p>Let&#39;s take the example of writing a test for the entire App to show you how to write a <strong>snapshot test</strong>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// App.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;

<span class="hljs-keyword">import</span> renderer <span class="hljs-keyword">from</span> <span class="hljs-string">'react-test-renderer'</span>;
...


describe(<span class="hljs-string">'App'</span>, () =&gt; {
  ...

  test(<span class="hljs-string">'snapShot testing'</span>, () =&gt; {
    <span class="hljs-keyword">const</span> tree = renderer.create(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>)</span>.toJSON();
    expect(tree).toMatchSnapshot();
  });

});
</code></pre>
<p>If this is the first time this test run, Jest will create a snapshot file(a folder &quot;<code>__snapshots__</code>&quot; will create as well) looks similar to this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592270773683/0diTT247Z.png" alt="snapshot-file-tree.png"></p>
<pre><code class="lang-snap">// App.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App snapShot testing 1`] = `
&lt;div
  className="App"
&gt;
  &lt;div
    className="navbar"
  &gt;
    ....
</code></pre>
<p>With this test in place, once you make any change over the DOM, the test will fail and show you exactly what is changed in a prettified format, like the output below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592240694406/KMc-AMj2S.png" alt="snapshot-test-error.png"></p>
<p>In this case, you can either press <strong><code>u</code></strong> to update the snapshot or change your code to make the test pass again.</p>
<blockquote>
<p>If you adding <strong>snapshot test</strong> at the early stage of development, you might want to turn off the test for a while by adding <strong><code>x</code></strong>  in front of <code>test</code>, to avoid getting too many errors and slowing down the process.</p>
<pre><code class="lang-javascript"> xtest(<span class="hljs-string">'should have no accessiblity violations'</span>, <span class="hljs-keyword">async</span> () =&gt; {
   ...
  });
</code></pre>
</blockquote>
<h2 id="third-let-s-see-how-to-test-a-ui-with-an-api-call-">Third, let&#39;s see how to test a UI with an API call.</h2>
<p>It is fairly common now a frontend UI has to fetch some data from an API before it renders its page. Writing tests about it becomes more essential for the Front End development today.</p>
<p>Now, let&#39;s look at the process and think about how we can test it.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592293783946/sD9oZkm-y.png" alt="web-api-datafetch.png"></p>
<ol>
<li>When a condition is met (such as click on a button or page loaded), an API call will be triggered;</li>
<li>When data come back from API, usually response need to parse before going to next step (optional);</li>
<li>When having proper data, the browser starts to render the data accordingly;</li>
<li>On the other hand, if something goes wrong, an error message should show up in the browser.</li>
</ol>
<p>In FrontEnd development, we can test things like below:</p>
<ul>
<li>whether the response comes back being correctly parsed?</li>
<li>whether the data is correctly rendered in the browser in the right place?</li>
<li>whether the browser show error message when something goes wrong?</li>
</ul>
<p>However, we should not:</p>
<ul>
<li>Test the API call</li>
<li>Call the real API for testing</li>
</ul>
<blockquote>
<p>Because most of the time, API is hosted by the third party, the time to fetch data is uncontrollable. Besides, for some APIs, given the same parameters, the data come back may vary, which will make the test result unpredictable. </p>
</blockquote>
<p>For testing with an API, we should:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592378921052/vjIjJkGnu.png" alt="web-api-datafetch-Test.png"></p>
<ul>
<li>Use Mock API for testing and return fack data</li>
<li>Use fake data to compare UI elements to see if they match</li>
</ul>
<p><strong><em>If you got the ideas, let&#39;s dive into the real code practice.</em></strong></p>
<p>Let&#39;s say we want to test the following <strong>News page component</strong>, where it gets the news from <code>getNews</code> API call and render them on the browser.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./page/News.js</span>
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> getNews <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/getNews'</span>;
<span class="hljs-keyword">import</span> NewsTable <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/newsTable'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; {
  <span class="hljs-keyword">const</span> [posts, setPosts] = useState([]);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [errorMsg, setErrorMsg] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> subreddit = <span class="hljs-string">'reactjs'</span>;

  useEffect(() =&gt; {
    getNews(subreddit)
      .then(res =&gt; {
        <span class="hljs-keyword">if</span> (res.length &gt; <span class="hljs-number">0</span>) {
          setPosts(res);
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'No such subreddit!'</span>);
        }
      })
      .catch(e =&gt; {
        setErrorMsg(e.message);
      })
      .finally(() =&gt; {
        setLoading(<span class="hljs-literal">false</span>);
      });
  }, [])

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>What is News Lately?<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {loading &amp;&amp; 'Loading news ...'}
        {errorMsg &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{errorMsg}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}
        {!errorMsg &amp;&amp; !loading &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">NewsTable</span> <span class="hljs-attr">news</span>=<span class="hljs-string">{posts}</span> <span class="hljs-attr">subreddit</span>=<span class="hljs-string">{subreddit}</span> /&gt;</span>}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span>
  )</span>
}
</code></pre>
<p>First, let&#39;s create a <code>__mocks__</code> folder at where the API call file located. (In our case, the API call file call <code>getNews.js</code>), create the mock API call file with the same name in this folder. Finally, prepare some mock data inside this folder. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592631443736/VjIgjiMfo.png" alt="mock_api_files.png"></p>
<p><strong>Mock API </strong> file(<code>getNews.js</code>) should look sth like below - </p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./helpers/__mocks__/getNews.js</span>
<span class="hljs-keyword">import</span> mockPosts <span class="hljs-keyword">from</span> <span class="hljs-string">'./mockPosts_music.json'</span>;

<span class="hljs-comment">// Check if you are using the mock API file, can remove it later</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'use mock api'</span>); 

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; <span class="hljs-built_in">Promise</span>.resolve(mockPosts);
</code></pre>
<p><strong>Vs. Real API Call</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./helpers/getNews.js</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> dayjs <span class="hljs-keyword">from</span> <span class="hljs-string">'dayjs'</span>;

<span class="hljs-comment">// API Reference - https://reddit-api.readthedocs.io/en/latest/#searching-submissions</span>

<span class="hljs-keyword">const</span> BASE_URL = <span class="hljs-string">'https://api.pushshift.io/reddit/submission/search/'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (subreddit) =&gt; {
  <span class="hljs-keyword">const</span> threeMonthAgo = dayjs().subtract(<span class="hljs-number">3</span>, <span class="hljs-string">'months'</span>).unix();
  <span class="hljs-keyword">const</span> numberOfPosts = <span class="hljs-number">5</span>;

  <span class="hljs-keyword">const</span> url = <span class="hljs-string">`<span class="hljs-subst">${BASE_URL}</span>?subreddit=<span class="hljs-subst">${subreddit}</span>&amp;after=<span class="hljs-subst">${threeMonthAgo}</span>&amp;size=<span class="hljs-subst">${numberOfPosts}</span>&amp;sort=desc&amp;sort_type=score`</span>;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(url);
    <span class="hljs-keyword">if</span> (response.status === <span class="hljs-number">200</span>) {
      <span class="hljs-keyword">return</span> response.data.data.reduce((result, post) =&gt; {
        result.push({
          id: post.id,
          title: post.title,
          full_link: post.full_link,
          created_utc: post.created_utc,
          score: post.score,
          num_comments: post.num_comments,
          author: post.author,
        });
        <span class="hljs-keyword">return</span> result;
      }, []);
    }
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(error.message);
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
};
</code></pre>
<p>As we can see from the above codes, a <strong>mock API call</strong> just simply return a resolved mock data, while a <strong>real API call</strong> need to go online and fetch data every time the test run.</p>
<p>With the mock API and mock data ready, we now start to write tests.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./page/News.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { render, screen, act } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> { BrowserRouter <span class="hljs-keyword">as</span> Router } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> News <span class="hljs-keyword">from</span> <span class="hljs-string">'./News'</span>;

jest.mock(<span class="hljs-string">'../helpers/getNews'</span>);  <span class="hljs-comment">//adding this line before any test.</span>

<span class="hljs-comment">// I make this setup function to simplify repeated code later use in tests.</span>
<span class="hljs-keyword">const</span> setup = (component) =&gt; (
  render(
   <span class="hljs-comment">// for react-router working properly in this component</span>
  <span class="hljs-comment">// if you don't use react-router in your project, you don't need it.</span>
    &lt;Router&gt;
      {component}
    &lt;<span class="hljs-regexp">/Router&gt;
  )
);

...</span>
</code></pre>
<blockquote>
<p>Please Note: </p>
<pre><code class="lang-javascript">jest.mock(<span class="hljs-string">'../helpers/getNews'</span>);
</code></pre>
<p>Please add the above code at the beginning of every test file that would possibly trigger the API call, not just the API test file. I make this mistake at the beginning without any notifications, until I add <code>console.log(&#39;call real API&#39;)</code> to monitor calls during the test.</p>
</blockquote>
<p>Next, we start to write a simple test to check whether a title and loading message are shown correctly.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./page/News.test.js</span>
...
describe(<span class="hljs-string">'News Page'</span>, () =&gt; {
  test(<span class="hljs-string">'load title and show status'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    setup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">News</span> /&gt;</span>)</span>;  <span class="hljs-comment">//I use setup function to simplify the code.</span>
    screen.getByText(<span class="hljs-string">'What is News Lately?'</span>); <span class="hljs-comment">// check if the title show up</span>
    <span class="hljs-keyword">await</span> waitForElementToBeRemoved(() =&gt; screen.getByText(<span class="hljs-string">'Loading news ...'</span>));
  });
...
});
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592468436356/YrV-Pbjrp.png" alt="mock_api_first_test_pass.png"></p>
<p>With the mock API being called and page rendering as expected. We can now continue to write more complex tests.</p>
<pre><code class="lang-javascript">...
test(<span class="hljs-string">'load news from api correctly'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    setup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">News</span> /&gt;</span>)</span>;
    screen.getByText(<span class="hljs-string">'What is News Lately?'</span>);

    <span class="hljs-comment">// wait for API get data back</span>
    <span class="hljs-keyword">await</span> waitForElementToBeRemoved(() =&gt; screen.getByText(<span class="hljs-string">'Loading news ...'</span>));

    screen.getByRole(<span class="hljs-string">"table"</span>);  <span class="hljs-comment">//check if a table show in UI now</span>
    <span class="hljs-keyword">const</span> rows = screen.getAllByRole(<span class="hljs-string">"row"</span>);  <span class="hljs-comment">// get all news from the table</span>

    mockNews.forEach((post, index) =&gt; {
      <span class="hljs-keyword">const</span> row = rows[index + <span class="hljs-number">1</span>];  <span class="hljs-comment">// ignore the header row</span>

       <span class="hljs-comment">// use 'within' limit search range, it is possible have same author for different post</span>
      within(row).getByText(post.title);  <span class="hljs-comment">// compare row text with mock data </span>
      within(row).getByText(post.author); 
    })

    expect(getNews).toHaveBeenCalledTimes(<span class="hljs-number">1</span>); <span class="hljs-comment">// I expect the Mock API only been call once</span>
    screen.debug(); <span class="hljs-comment">// Optionally, you can use debug to print out the whole dom</span>
  });
...
</code></pre>
<blockquote>
<p>Please Note </p>
<pre><code class="lang-javascript"> expect(getNews).toHaveBeenCalledTimes(<span class="hljs-number">1</span>);
</code></pre>
<p>This code is essential here to ensure the API call is only called as expected.</p>
</blockquote>
<p>When this API call test passes accordingly, we can start to explore something more exciting! </p>
<p><strong>As we all know, an API call can go wrong sometimes due to various reasons, how are we gonna test it?</strong></p>
<p>To do that, we need to re-write our <code>mock API file</code> first -</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// // ./helpers/__mocks__/getNews.js</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'use mock api'</span>);  <span class="hljs-comment">// optionally put here to check if the app calling the Mock API</span>
<span class="hljs-comment">// check more about mock functions at https://jestjs.io/docs/en/mock-function-api</span>
<span class="hljs-keyword">const</span> getNews = jest.fn().mockResolvedValue([]); 
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> getNews;
</code></pre>
<p>Then we need to re-write the setup function in <code>News.test.js</code> file.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./page/News.test.js</span>
...
<span class="hljs-comment">// need to import mock data and getNews function</span>
<span class="hljs-keyword">import</span> mockNews <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/__mocks__/mockPosts_music.json'</span>;
<span class="hljs-keyword">import</span> getNews <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/getNews'</span>;
...
<span class="hljs-comment">// now we need to pass state and data to the initial setup</span>
<span class="hljs-keyword">const</span> setup = (component,  state = <span class="hljs-string">'pass'</span>, data = mockNews) =&gt; {
  <span class="hljs-keyword">if</span> (state === <span class="hljs-string">'pass'</span>) {
    getNews.mockResolvedValueOnce(data);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (state === <span class="hljs-string">'fail'</span>) {
    getNews.mockRejectedValue(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(data[<span class="hljs-number">0</span>]));
  }

  <span class="hljs-keyword">return</span> (
    render(
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Router</span>&gt;</span>
        {component}
      <span class="hljs-tag">&lt;/<span class="hljs-name">Router</span>&gt;</span>
    )</span>)
};
...
</code></pre>
<p>I pass the default values into the setup function here, so you don&#39;t have to change previous tests. But I do suggest pass them in the test instead to make the tests more readable.</p>
<p>Now, let&#39;s write the test for API failing.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./page/News.test.js</span>
...
test(<span class="hljs-string">'load news with network errors'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// pass whatever error message you want here.</span>
    setup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">News</span> /&gt;</span>, 'fail', ['network error']);
    screen.getByText('What is News Lately?');

    await waitForElementToBeRemoved(() =&gt; screen.getByText('Loading news ...'));
    screen.getByText('network error');

    expect(getNews).toHaveBeenCalledTimes(1);
  })
...</span>
</code></pre>
<p>Finally, you can find the complete test code from <a target='_blank' rel='noopener noreferrer'  href="https://github.com/kelvin8773/react-test-examples/blob/master/src/pages/News.test.js">here</a>. </p>
<blockquote>
<p><strong>Please Note</strong>
The above examples are just simple test cases for demonstration purposes, in real-world scenarios, the tests would be much more complex. You can check out more testing examples from my other project <a target='_blank' rel='noopener noreferrer'  href="https://github.com/ooloo-io/reddit-timer-kelvin8773">here</a>.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1592392936970/-wATYODAx.jpeg" alt="final_lesson.jpg">
<strong>Photo by <a target='_blank' rel='noopener noreferrer'  href="https://unsplash.com/@thisisengineering">ThisisEngineering RAEng</a> on <a target='_blank' rel='noopener noreferrer'  href="https://unsplash.com/">Unsplash</a></strong></p>
<h2 id="final-words">Final words</h2>
<p>I followed the best practices <strong>Kent C. Dodds</strong> suggested in his blog post - <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library">Common mistakes with React Testing Library</a> published in May 2020, in which you might find my code is slightly different from <a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/react-testing-library/example-intro">Test-Library Example</a> (I think soon Kent will update the docs as well),  but I believe that should be how we write the test in 2020 and onward. </p>
<p>I use both <a target='_blank' rel='noopener noreferrer'  href="https://styled-components.com/">styled-componet</a> and in-line style in this project to make UI look better, but it is not necessary, you are free to use whatever CSS framework in react, it should not affect the tests. </p>
<p>Finally, Testing is an advanced topic in FrontEnd development, I only touch very few aspects of it and I am still learning. If you like me, just starting out, I would suggest you use the examples I show here and in <a target='_blank' rel='noopener noreferrer'  href="https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-1-ckawd0emy07rvbbs1p02tayit">my previous article</a> to play around with your personal projects. Once you master the fundamentals, you can start to explore more alternatives on the market to find the best fit for your need.</p>
<h3 id="here-are-some-resources-i-recommend-to-continue-learning-">Here are some resources I recommend to continue learning:</h3>
<ul>
<li><a target='_blank' rel='noopener noreferrer'  href="https://create-react-app.dev/docs/running-tests">Testing from Create React App</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/guide-which-query">Which Query should I use From Testing Library</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/example-codesandbox">More examples from Testing Library</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://redux.js.org/recipes/writing-tests">Write Test for Redux from Redux.js</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://www.gatsbyjs.org/docs/unit-testing/">Unit Test From Gatsby.js</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/effective-snapshot-testing">Effective Snapshot Testing</a> from <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/">Kent C.Dodds</a>.</li>
</ul>
<h3 id="resources-and-article-i-referenced-to-finished-this-article-">Resources and Article I referenced to finished this article:</h3>
<ul>
<li><a target='_blank' rel='noopener noreferrer'  href="https://dev.to/jkettmann/inside-a-dev-s-mind-refactoring-and-debugging-a-react-test-2jap">Inside a dev’s mind — Refactoring and debugging a React test</a> By <a target='_blank' rel='noopener noreferrer'  href="https://jkettmann.com/">Johannes Kettmann</a>.</li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://jkettmann.com/dont-useeffect-as-callback/">Don&#39;t useEffect as callback!</a> by  <a target='_blank' rel='noopener noreferrer'  href="https://jkettmann.com/">Johannes Kettmann</a>.</li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library">Common mistakes with React Testing Library</a> by <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/">Kent C.Dodds</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning">Fix the not wrapped act warning</a> by <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/">Kent C.Dodds</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://reactjs.org/docs/accessibility.html">Accessibility From React</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://www.npmjs.com/package/jest-axe">Axe for Jest</a></li>
</ul>
<h3 id="special-thanks-for-johannes-kettmann-https-jkettmann-com-and-his-course-ooloo-io-https-ooloo-io-">Special Thanks for <a target='_blank' rel='noopener noreferrer'  href="https://jkettmann.com/">Johannes Kettmann</a> and his course <a target='_blank' rel='noopener noreferrer'  href="https://ooloo.io">ooloo.io</a>.</h3>
<blockquote>
<p>I have learned a lot in the past few months from the course and some study fellows from the course - <a target='_blank' rel='noopener noreferrer'  href="https://github.com/martink-rsa">Martin Kruger</a> and <a target='_blank' rel='noopener noreferrer'  href="https://github.com/ProxN">ProxN</a>,  who help inspire me a lot to finish this testing series articles.</p>
<h4 id="below-are-what-i-have-learned-">Below are what I have learned,</h4>
<ul>
<li>Creating pixel-perfect designs</li>
<li>Planning and implementing a complex UI component</li>
<li>Implement data fetching with error handling</li>
<li>Debugging inside an IDE</li>
<li>Writing integration tests</li>
<li>Professional Git workflow with pull requests</li>
<li>Code reviews</li>
<li>Continuous integration</li>
</ul>
<p>This is the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/ooloo-io/reddit-timer-kelvin8773">finishing project</a> as the outcome.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[How to write tests for React in 2020 - part 1]]></title><description><![CDATA[Write React Test with react recommend libraries — Jest & React Testing Library for a complete beginner.


 From https://reactjs.org/docs/test-utils.html#overview
This article is intended to who just start to learn React and wonder how to write some s...]]></description><link>https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-1</link><guid isPermaLink="true">https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-1</guid><category><![CDATA[React]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Jest]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Kelvin Liang]]></dc:creator><pubDate>Mon, 01 Jun 2020 10:37:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1591007744043/7drL6tHAU.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Write React Test with react recommend libraries — <a target='_blank' rel='noopener noreferrer'  href="https://jestjs.io/docs/en/tutorial-react">Jest</a> &amp; <a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a> for a complete beginner.</p>
</blockquote>
<p><img src="https://cdn.filestackcontent.com/siFmmSFQFeJWjyzug1Xq" alt="screenshot from React docs">
 <strong><em>From <a target='_blank' rel='noopener noreferrer'  href="https://reactjs.org/docs/test-utils.html#overview">https://reactjs.org/docs/test-utils.html#overview</a></em></strong></p>
<p>This article is intended to who just start to learn React and wonder how to write some simple tests with their React applications. And just like most of the people start to create React app using <code>create-react-app</code>, I would start with it as well.</p>
<h2 id="first-let-s-start-with-the-default-example-">First, let&#39;s start with the default example.</h2>
<p><strong>Default Dependencies with <code>create-react-app</code> (2020/05/22)</strong> </p>
<pre><code class="lang-json"><span class="hljs-string">"dependencies"</span>: {
    "<span class="hljs-attr">@testing-library/jest-dom</span>": <span class="hljs-string">"^4.2.4"</span>,
    "<span class="hljs-attr">@testing-library/react</span>": <span class="hljs-string">"^9.3.2"</span>,
    "<span class="hljs-attr">@testing-library/user-event</span>": <span class="hljs-string">"^7.1.2"</span>,
    "<span class="hljs-attr">react</span>": <span class="hljs-string">"^16.13.1"</span>,
    "<span class="hljs-attr">react-dom</span>": <span class="hljs-string">"^16.13.1"</span>,
    "<span class="hljs-attr">react-scripts</span>": <span class="hljs-string">"3.4.1"</span>
  }
</code></pre>
<p>There is one test already written to help you to start. </p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/App.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;

test(<span class="hljs-string">'renders learn react link'</span>, () =&gt; {
  <span class="hljs-keyword">const</span> { getByText } = render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>)</span>; <span class="hljs-comment">//render is from @testing-library/react</span>
  <span class="hljs-keyword">const</span> linkElement = getByText(<span class="hljs-regexp">/learn react/i</span>);
  expect(linkElement).toBeInTheDocument(); <span class="hljs-comment">//expect assertion is from Jest</span>
});
</code></pre>
<p>If you run the command <code>$ yarn test App</code>, you will see a similar result as the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1591008239249/CElHSgF6v.png" alt="Testing Result from default case"></p>
<p>With the default <code>create-react-app</code> setting, you can start to write a test without install or configure anything.</p>
<p>From the example above, we should learn -</p>
<ul>
<li><strong>Where and how I can put my test files?</strong> - as you can see <code>App.test.js</code> file is put next to <code>App.js</code> file in the same folder, and it put <code>.test.js</code> suffix after <code>App</code> component name as its filename.  It is the default conventions suggested by <code>create-react-app</code> team (<a target='_blank' rel='noopener noreferrer'  href="https://create-react-app.dev/docs/running-tests/#filename-conventions">link here</a>).</li>
</ul>
<ul>
<li><strong><a target='_blank' rel='noopener noreferrer'  href="https://jestjs.io/docs/en/tutorial-react">Jest</a> and <a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/react">React Testing Library</a> are the tools chain behind the test</strong>. They both are ship with create-react-app by default.</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// setupTests.js</span>
<span class="hljs-comment">// Jest is importing from a global setup file if you wonder</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'@testing-library/jest-dom/extend-expect'</span>;
</code></pre>
<hr>
<h2 id="second-write-a-test-for-navbar-component-">Second, write a test for NavBar component.</h2>
<p>I am creating a <code>NavBar</code> component that contains links and logo in it. </p>
<p>First, I would start writing test without writing the actual component (Test Drive Development).</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// navBar.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; 
<span class="hljs-comment">// use "screen" - a newer way to utilize query in 2020 </span>
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>; 
<span class="hljs-keyword">import</span> NavBar <span class="hljs-keyword">from</span> <span class="hljs-string">'./navBar'</span>; <span class="hljs-comment">// component to test</span>

test(<span class="hljs-string">'render about link'</span>, () =&gt; {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NavBar</span> /&gt;</span>)</span>;
  expect(screen.getByText(<span class="hljs-regexp">/about/</span>)).toBeInTheDocument();
})
</code></pre>
<p>The test will fail first since I didn&#39;t write any code in <code>navBar.js</code> component yet. </p>
<p><img src="https://cdn.filestackcontent.com/d9w6rvNIT1y6HjzR3i9v" alt="second test result - fail"></p>
<p>With code below in <code>navBar.js</code>, the test should pass now.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// navBar.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> NavBar = () =&gt; (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>
      about
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
)</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NavBar;
</code></pre>
<p>For now, you should learn: </p>
<ul>
<li><p><code>expect( ... ).toBeInTheDocument()</code> assertion is from Jest.</p>
</li>
<li><p><code>render(&lt;NavBar /&gt;);</code> and <code>screen.getByText(/about/)</code> is from Testing Library.</p>
</li>
<li><p>Jest and React Testing Library work together to make writing tests in React easy.</p>
</li>
<li><p><code>screen.getByText(/about/)</code> use &quot;getByText&quot; instead of select by class name is because React Testing Library adapting the mindset of focus on User experiences over implementation detail.</p>
</li>
<li><p>To learn more to expand and alter the test, you can check out the following resources:</p>
<ul>
<li><a target='_blank' rel='noopener noreferrer'  href="https://jestjs.io/docs/en/tutorial-react">Jest Testing with React App Tutorial</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/react-testing-library/cheatsheet">React Testing Library syntax cheatsheet</a></li>
</ul>
</li>
</ul>
<p>Now let’s expand the test and component to make it more real -</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// navBar.test.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> NavBar <span class="hljs-keyword">from</span> <span class="hljs-string">'./navBar'</span>;

<span class="hljs-comment">// include as many test cases as you want here</span>
<span class="hljs-keyword">const</span> links = [
  { text: <span class="hljs-string">'Home'</span>, location: <span class="hljs-string">"/"</span> },
  { text: <span class="hljs-string">'Contact'</span>, location: <span class="hljs-string">"/contact"</span> },
  { text: <span class="hljs-string">'About'</span>, location: <span class="hljs-string">"/about"</span> },
  { text: <span class="hljs-string">'Search'</span>, location: <span class="hljs-string">"/search"</span> },
];

<span class="hljs-comment">// I use test.each to iterate the test cases above</span>
test.each(links)(
  <span class="hljs-string">"Check if Nav Bar have %s link."</span>,
  (link) =&gt; {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NavBar</span> /&gt;</span>)</span>;
<span class="hljs-comment">//Ensure the text is in the dom, will throw error it can't find</span>
    <span class="hljs-keyword">const</span> linkDom = screen.getByText(link.text); 

<span class="hljs-comment">//use jest assertion to verify the link property</span>
    expect(linkDom).toHaveAttribute(<span class="hljs-string">"href"</span>, link.location);
  }
);

test(<span class="hljs-string">'Check if have logo and link to home page'</span>, () =&gt; {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NavBar</span> /&gt;</span>)</span>;
        <span class="hljs-comment">// get by TestId define in the navBar</span>
    <span class="hljs-keyword">const</span> logoDom = screen.getByTestId(<span class="hljs-regexp">/company-logo/</span>); 
        <span class="hljs-comment">// check the link location</span>
    expect(logoDom).toHaveAttribute(<span class="hljs-string">"href"</span>, <span class="hljs-string">"/"</span>); 
    <span class="hljs-comment">//check the logo image</span>
  expect(screen.getByAltText(<span class="hljs-regexp">/Company Logo/</span>)).toBeInTheDocument(); 
});
</code></pre>
<p>This is what a NavBar component usually look like (maybe need to add some styles).</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// navBar.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> NavBar = () =&gt; (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">data-testid</span>=<span class="hljs-string">"company-logo"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/logo.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Company Logo"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span> Home <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span> About <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/contact"</span>&gt;</span> Contact <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/search"</span>&gt;</span> Search <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
)</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NavBar;
</code></pre>
<hr>
<h2 id="third-write-a-signup-form-component-test-">Third, write a signup form component test.</h2>
<p>After writing a test for static content, let&#39;s write a test for more dynamic content - a signup form. </p>
<p>First, let&#39;s think in TDD way - what we need in this signup form (no matter how it look):</p>
<ul>
<li>An input field for name, which only allows string between 3 to 30 long.</li>
<li>An input field for email, which can check whether it is a valid email address.</li>
<li>An input field for password, which can check its complexity (at least 1 number, 1 string in lower case, 1 string in upper case, 1 special character)</li>
<li>A submit button.</li>
<li>All 3 inputs above are required, can&#39;t be empty.</li>
</ul>
<p>Now, let&#39;s write the test.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/*  Prepare some test cases, ensure 90% edge cases are covered.
    You can always change your test cases to fit your standard
*/</span>
<span class="hljs-keyword">const</span> entries = [
  { name: <span class="hljs-string">'John'</span>, email: <span class="hljs-string">'john_doe@yahoo'</span>, password: <span class="hljs-string">'helloworld'</span> },
  { name: <span class="hljs-string">'Jo'</span>, email: <span class="hljs-string">'jo.msn.com'</span>, password: <span class="hljs-string">'pa$$W0rd'</span> },
  { name: <span class="hljs-string">''</span>, email: <span class="hljs-string">'marry123@test.com'</span>, password: <span class="hljs-string">'123WX&amp;abcd'</span> },
  { name: <span class="hljs-string">'kent'</span>.repeat(<span class="hljs-number">10</span>), email: <span class="hljs-string">'kent@testing.com'</span>, password: <span class="hljs-string">'w%oRD123yes'</span> },
  { name: <span class="hljs-string">'Robert'</span>, email: <span class="hljs-string">'robert_bell@example.com'</span>, password: <span class="hljs-string">'r&amp;bsEc234E'</span> },
]
</code></pre>
<p>Next, build up the skull of the test.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// signupForm.test.js</span>
<span class="hljs-comment">// this mostly a input validate test</span>
describe(<span class="hljs-string">'Input validate'</span>, () =&gt; {
  <span class="hljs-comment">/* 
   I use test.each to iterate every case again
   I need use 'async' here because wait for 
   validation is await function 
  */</span>     
  test.each(entries)(<span class="hljs-string">'test with %s entry'</span>, <span class="hljs-keyword">async</span> (entry) =&gt; { 
    ...

    })
})
</code></pre>
<p>Now, let building the block inside the test.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// signupForm.test.js</span>
...
test.each(entries)(<span class="hljs-string">'test with %s entry'</span>, <span class="hljs-keyword">async</span> (entry) =&gt; { 
<span class="hljs-comment">//render the component first (it will clean up for every iteration    </span>
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SignupForm</span> /&gt;</span>)</span>; 

<span class="hljs-comment">/*  grab all the input elements. 
I use 2 queries here because sometimes you can choose
how your UI look (with or without Label text) without
breaking the tests
*/</span>     
    <span class="hljs-keyword">const</span> nameInput = screen.queryByLabelText(<span class="hljs-regexp">/name/i</span>)
      || screen.queryByPlaceholderText(<span class="hljs-regexp">/name/i</span>);
    <span class="hljs-keyword">const</span> emailInput = screen.getByLabelText(<span class="hljs-regexp">/email/i</span>)
      || screen.queryByPlaceholderText(<span class="hljs-regexp">/email/i</span>);
    <span class="hljs-keyword">const</span> passwordInput = screen.getByLabelText(<span class="hljs-regexp">/password/i</span>)
      || screen.queryByPlaceholderText(<span class="hljs-regexp">/password/i</span>);

<span class="hljs-comment">/* use fireEvent.change and fireEvent.blur to change name input value
and trigger the validation
*/</span>
    fireEvent.change(nameInput, { target: { value: entry.name } }); 
    fireEvent.blur(nameInput); 

<span class="hljs-comment">/* first if-statement to check whether the name is input.
second if-statement to check whether the name is valid.
'checkName' is a utility function you can define by yourself.
I use console.log here to show what is being checked.  
*/</span>
<span class="hljs-keyword">if</span> (entry.name.length === <span class="hljs-number">0</span>) {
      expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/name is required/i</span>)).not.toBeNull();
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'name is required.'</span>);
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!checkName(entry.name)) {
      <span class="hljs-comment">// if the name is invalid, error msg will showup somewhere in the form</span>
      expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/invalid name/i</span>)).not.toBeNull();
      <span class="hljs-built_in">console</span>.log(entry.name + <span class="hljs-string">' is invalid name.'</span>);
    };

<span class="hljs-comment">// With a similar structure, you can continue building the rest of the test.</span>
        ...

<span class="hljs-comment">/*  Remember to add this line at the end of your test to 
avoid act wrapping warning.
More detail please checkout Kent C.Dodds's post:
(He is the creator of Testing Library)
https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning
*/</span>
     <span class="hljs-keyword">await</span> act(() =&gt; <span class="hljs-built_in">Promise</span>.resolve()); 
})
...
</code></pre>
<blockquote>
<p>For the complete Testing code, please find them <a target='_blank' rel='noopener noreferrer'  href="https://github.com/kelvin8773/react-test-examples/blob/master/src/components/signupForm.test.js">here</a>.</p>
</blockquote>
<p>Ok, now the test is done (maybe we will come back to tweak a bit, but let&#39;s move on for now), let&#39;s write the component.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// signupForm.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-comment">/* 
I borrow the sample code from formik library with some adjustments
https://jaredpalmer.com/formik/docs/overview#the-gist
*/</span>
<span class="hljs-keyword">import</span> { Formik } <span class="hljs-keyword">from</span> <span class="hljs-string">'formik'</span>;
<span class="hljs-comment">/* 
For validation check, I wrote 3 custom functions.
(I use the same functions in test)
*/</span>
<span class="hljs-keyword">import</span> {
  checkName,
  checkEmail,
  checkPassword,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'../utilities/check'</span>;

<span class="hljs-keyword">const</span> SignupForm = () =&gt; (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Anywhere in your app!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Formik</span>
      <span class="hljs-attr">initialValues</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">name:</span> '', <span class="hljs-attr">email:</span> '', <span class="hljs-attr">password:</span> '' }}
      <span class="hljs-attr">validate</span>=<span class="hljs-string">{values</span> =&gt;</span> {
        const errors = {};
        if (!values.name) {
          errors.name = 'Name is Required'
        } else if (!checkName(values.name)) {
          errors.name = `invalid name`;
        }

        if (!values.email) {
          errors.email = 'Email is Required';
        }
        else if (!checkEmail(values.email)) {
          errors.email = 'Invalid email address';
        }

        if (!values.password) {
          errors.password = 'Password is Required';
        } else if (!checkPassword(values.password)) {
          errors.password = 'Password is too simple';
        }

        return errors;
      }}
      onSubmit={(values, { setSubmitting }) =&gt; {
        setTimeout(() =&gt; {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    &gt;
      {({
        values,
        errors,
        touched,
        handleChange,
        handleBlur,
        handleSubmit,
        isSubmitting,
        /* and other goodies */
      }) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
              Name:
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your name here"</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                <span class="hljs-attr">onBlur</span>=<span class="hljs-string">{handleBlur}</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{values.name}</span>
              /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> '<span class="hljs-attr">color</span>'<span class="hljs-attr">:</span> '<span class="hljs-attr">red</span>' }}&gt;</span>
              {errors.name &amp;&amp; touched.name &amp;&amp; errors.name}
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
              Email:
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Email Address"</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                <span class="hljs-attr">onBlur</span>=<span class="hljs-string">{handleBlur}</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{values.email}</span>
              /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> '<span class="hljs-attr">color</span>'<span class="hljs-attr">:</span> '<span class="hljs-attr">red</span>' }}&gt;</span>
              {errors.email &amp;&amp; touched.email &amp;&amp; errors.email}
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
              Password:
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"password here"</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                <span class="hljs-attr">onBlur</span>=<span class="hljs-string">{handleBlur}</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{values.password}</span>
              /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> '<span class="hljs-attr">color</span>'<span class="hljs-attr">:</span> '<span class="hljs-attr">red</span>' }}&gt;</span>
              {errors.password &amp;&amp; touched.password &amp;&amp; errors.password}
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{isSubmitting}</span>&gt;</span>
              Submit
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        )</span>}
    &lt;<span class="hljs-regexp">/Formik&gt;
  &lt;/</span>div&gt;
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SignupForm;
</code></pre>
<p>And the form will look similar like below (no much style, but good enough for our purpose), And with wrong input, the error message will show below the input:</p>
<p><img src="https://cdn.filestackcontent.com/VcJvmmqT6q6K4TVKeA2l" alt="Form screenshot with validation"></p>
<p>If you finished the test above, now the test should all pass, run <code>yarn test --verbose</code> in your command line, you should see something similar like this.with the verbose option and console.log message, you can see how each case is being tested and which one is a good case and which one is not.</p>
<p><img src="https://cdn.filestackcontent.com/D6IJHgbQTWx0XDj56kMc" alt="Final form test Result"></p>
<blockquote>
<p>For more testing code examples and different cases, please check out my repo <a target='_blank' rel='noopener noreferrer'  href="https://github.com/kelvin8773/react-test-examples">here</a>. </p>
</blockquote>
<h2 id="-final-image-jpeg-https-cdn-hashnode-com-res-hashnode-image-upload-v1591008094404-6zkxz41d3-jpeg-"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1591008094404/6zKxz41D3.jpeg" alt="final-image.jpeg"></h2>
<h2 id="final-words-">Final words.</h2>
<p>It is difficult for a beginner to learn all of it once so just slow down if it&#39;s overwhelming. It took me at least an entire week to learn the basics, and this is just the beginning of writing tests for React applications.</p>
<p>It is a hard topic to grasp, but I believe it is worthy to spend some time on it if you want to become a Pro FrontEnd developer. </p>
<p>And the good news is, you have a good start, you should now know how to leverage <strong>Jest</strong> and <strong>React Testing Library</strong> to write tests around your react components, and you can start to explore other libraries and solutions out there with this good foundation. </p>
<p>If you like to learn more about React Testing, I have another article <a target='_blank' rel='noopener noreferrer'  href="https://blog.kelvinliang.cn/how-to-write-tests-for-react-in-2020-part-2-ckbkmnpfv00iwavs14gu7ijjs">here</a> talking about <strong>accessibility test</strong>, <strong>snapshot test</strong>,  <strong>mock API test</strong>, and more, go check it once you master the tests here.</p>
<h3 id="resources-i-have-referred-to-this-article-">Resources I have referred to this article:</h3>
<ul>
<li><a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library">Common Mistakes with React Testing</a> by <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/">Kent C.Dodds</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning">Fix the not wrapped act warning</a> by <a target='_blank' rel='noopener noreferrer'  href="https://kentcdodds.com/">Kent C.Dodds</a></li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://medium.com/@boyney123/my-experience-moving-from-enzyme-to-react-testing-library-5ac65d992ce">My Experience moving From Enzyme to React Testing Library</a> (Opinion about which library to use for React Testing)</li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://testing-library.com/docs/recipes">Testing Library Recipes</a> (More resources learn about React Testing Library)</li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://dev.to/jkettmann/inside-a-dev-s-mind-refactoring-and-debugging-a-react-test-2jap">Inside a dev’s mind — Refactoring and debugging a React test</a> By <a target='_blank' rel='noopener noreferrer'  href="https://jkettmann.com/">Johannes Kettmann</a> (I started to learn React Test with this article, but it is much more advance, I will write more about it later)</li>
</ul>
<h3 id="special-thanks-to-ooloo-io-https-ooloo-io-and-johannes-kettmann-https-dev-to-jkettmann-">Special Thanks to <a target='_blank' rel='noopener noreferrer'  href="https://ooloo.io/">ooloo.io</a> and <a target='_blank' rel='noopener noreferrer'  href="https://dev.to/jkettmann">Johannes Kettmann</a>:</h3>
<blockquote>
<p>For someone who wants to become a job-ready FrontEnd developer, I would recommend trying a course from <a target='_blank' rel='noopener noreferrer'  href="https://ooloo.io/">ooloo.io</a>. It introduces concepts such as - Creating pixel-perfect design, Planning and implementing a complex UI component, Debugging inside IDE, and <a target='_blank' rel='noopener noreferrer'  href="https://dev.to/jkettmann/inside-a-dev-s-mind-refactoring-and-debugging-a-react-test-2jap">Writing integration tests</a>, which are not necessary would see from most of the online tutorials or courses. And Yes, I got a lot of inspiration from this course which helped me write up this article eventually.</p>
</blockquote>
<p><strong><em> This article originally posted in <a target='_blank' rel='noopener noreferrer'  href="https://dev.to/kelvin9877/how-to-write-tests-for-react-in-2020-4oai">dev.to</a> </em></strong></p>
]]></content:encoded></item><item><title><![CDATA[Why I should check my code's performance?]]></title><description><![CDATA[As a #CodeNewbie,  I usually don't care how I solved a coding challenge as long as I am getting the result (maybe I do care a bit about elegance, cleanness of the codes).  Until one day, I saw the differences between my code and my teammates' code:
C...]]></description><link>https://blog.kelvinliang.cn/why-i-should-check-my-codes-performance</link><guid isPermaLink="true">https://blog.kelvinliang.cn/why-i-should-check-my-codes-performance</guid><category><![CDATA[algorithms]]></category><category><![CDATA[Benchmark]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[Kelvin Liang]]></dc:creator><pubDate>Wed, 17 Jul 2019 15:39:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1562032752139/Hnihs3MrI.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>As a <strong>#CodeNewbie,</strong>  I usually don&#39;t care how I solved a coding challenge as long as I am getting the result (maybe I do care a bit about elegance, cleanness of the codes).  Until one day, I saw the differences between my code and my teammates&#39; code:</p>
<pre><code class="lang-shell">Comparison:
     Yunus&#39;s method :   119030.6 i/s
     Ebuka&#39;s method :    63232.0 i/s - 1.88x  slower
    Kelvin&#39;s method :     6848.0 i/s - 17.38x  slower
</code></pre>
<p>I changed my mind since. </p>
</blockquote>
<p>Below, I will explore the reasons why I think checking the performance of your code is important, and how we can do it.</p>
<h2 id="first-of-all-it-is-fun-">First of all, it is fun.</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1562237928237/3w0qFYZwF.jpeg" alt="Photo by Ilya Pavlov on Unsplash">
<em>Photo by Ilya Pavlov on Unsplash</em></p>
<p>When doing a coding challenge, it is fun to compete with your coding partners to see who can solve the same problem first. </p>
<p>The same logic applies for code performance checking, the differences are, not only you can compete with your coding partners or teammates, but also yourself, and even the team of the ruby contributors. </p>
<p>Below is one example -</p>
<ul>
<li>The challenge - return the median of an unsorted array (size of the array is always an odd number).</li>
</ul>
<pre><code class="lang-ruby"><span class="hljs-comment"># get the median by search every number in the array</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">median</span><span class="hljs-params">(array)</span></span>
  array.each <span class="hljs-keyword">do</span> |x|
    count = <span class="hljs-number">0</span>
    array.each {|y| count += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> y &lt;= x}
    <span class="hljs-keyword">return</span> x <span class="hljs-keyword">if</span> count == array.size/<span class="hljs-number">2</span> + <span class="hljs-number">1</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<pre><code class="lang-ruby"><span class="hljs-comment"># Use Ruby build-In sort to find the array</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">median_r</span><span class="hljs-params">(array)</span></span>
  array.sort[array.size/<span class="hljs-number">2</span>]  
<span class="hljs-keyword">end</span>
</code></pre>
<p>if run above methods in my laptop, I almost can get the same result at the same time. But are the codes really the same?  Thanks for the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/evanphx/benchmark-ips">benchmark tools</a> from <a target='_blank' rel='noopener noreferrer'  href="https://github.com/evanphx">Evan Phoenix</a>, I can benchmark these 2 blocks of codes from my laptop by just adding a few lines of code. </p>
<pre><code class="lang-ruby">Benchmark.ips <span class="hljs-keyword">do</span> |x|
    x.config(<span class="hljs-symbol">:time</span> =&gt; <span class="hljs-number">3</span>, <span class="hljs-symbol">:warmup</span> =&gt; <span class="hljs-number">2</span>)

    x.report(<span class="hljs-string">"Kelvin's Method"</span>) {median(list)}
    x.report(<span class="hljs-string">"Ruby Sort"</span>) {median_r(list)}

    x.compare!    
  <span class="hljs-keyword">end</span>
</code></pre>
<p><em>Please note - All coding block should be in the same file</em></p>
<p>The result - </p>
<pre><code class="lang-shell"># Benchmark test with [7, 3, 9, 8, 2, 1, 5]
Comparison:
               Ruby Sort:  1098170.4 i/s
     Kelvin&#39;s Method:   171681.3 i/s - 6.40x  slower
</code></pre>
<p>ok, so my method is slower than Ruby build-in method, that is reasonable, I just learn Ruby, I can&#39;t compete with the group sophisticate ruby developer. (I will try later !!)</p>
<p>How about my teammates&#39; code? </p>
<pre><code class="lang-shell"># Benchmark test with [7, 3, 9, 8, 2, 1, 5]
Comparison:
           Ruby Sort:  1098147.9 i/s
      Yunus&#39;s Method:   528263.9 i/s - 2.08x  slower
      Kelvin&#39;s Method:   173466.6 i/s - 6.33x  slower
</code></pre>
<p>Wow, my teammate&#39;s code ran 3 times faster than mine, I want to find out why and how. </p>
<p>Finally, I use my own sorting method (Advance Quick Sort) created another version of find median method.</p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">median_k1</span><span class="hljs-params">(array)</span></span>
  temp = array.clone .    <span class="hljs-comment">#for not modify the original array</span>
  advanced_quicksort(temp) 
  temp[array.size/<span class="hljs-number">2</span>]
<span class="hljs-keyword">end</span>
</code></pre>
<p>and run the benchmark test again.</p>
<pre><code class="lang-shell"># Benchmark test with [7, 3, 9, 8, 2, 1, 5]
Comparison:
           Ruby Sort:              1087509.0 i/s
      Yunus&#39;s Method:        525471.1 i/s - 2.07x  slower
     Kelvin&#39;s Method:         166969.8 i/s - 6.51x  slower
Kelvin&#39;s Adv Sort Method:    151304.6 i/s - 7.19x  slower
</code></pre>
<p>ok, it runs even slower, but it is fun when we have tried and see the result,  isn&#39;t it? (just like we watching horse racing,  but we competing with code but not horse)</p>
<p>Sometime, I will repeat the above processes over and over again until I can&#39;t think of any way to improve my code. </p>
<p>I might end up re-write my code or just have deeper understand the code I wrote. The point is,  I could write better, faster code later, I enjoy more learning to code, it gives me motivations to move forward.</p>
<h2 id="second-it-helps-me-learn-how-to-code-at-a-deeper-level-">Second, It helps me learn how to code at a deeper level.</h2>
<p>When doing the coding challenges with my team, we usually wouldn&#39;t notice the differences between solutions, therefore, we would never discuss whether one solution is better than the other, because they all give same results. </p>
<p>But with the comparisons, we suddenly have more topics to discuss and competing with, every team member wants to write faster code, then we will learn from each other.</p>
<p>For example, one of my team members tries to study which Ruby enumerable methods are more efficient given the same situation.  <a target='_blank' rel='noopener noreferrer'  href="https://medium.com/@YunusAybey/a784d01148f9">His Article</a></p>
<p>Also,  from the sample I mentioned earlier, once I saw the performance of my codes, I have the desires to dig deeper on how and why my code is run slower, even I just try to find a way to beat myself.</p>
<p>Beside comparing codes, actually, there is one more thing we can test - inputs (or DataSet), some code work better with one input but work worse with another. here is some example:</p>
<pre><code class="lang-shell"># Benchmark test with [7, 3, 9, 8, 2, 1, 5]
Comparison:
           Ruby Sort:  1041021.5 i/s
      Yunus&#39;s Method:   500895.3 i/s - 2.08x  slower
     Kelvin&#39;s Method:   165341.8 i/s - 6.30x  slower
    Kelvin&#39;s AdvSort:   141434.0 i/s - 7.36x  slower
</code></pre>
<p>The situation is changing with an almost sorted array...</p>
<pre><code class="lang-shell"># Benchmark test with [0, 1, 2, 4, 6, 5, 3, 8, 9]
Comparison:
           Ruby Sort:   926798.7 i/s
     Kelvin&#39;s Method:   253107.0 i/s - 3.66x  slower
      Yunus&#39;s Method:   117203.3 i/s - 7.91x  slower
    Kelvin&#39;s AdvSort:    68769.3 i/s - 13.48x  slower
</code></pre>
<p>The table flipped again with a larger dataset ...</p>
<pre><code class="lang-shell"># Benchmark test with an array with 100 unsorted numbers
Comparison:
           Ruby Sort:   108950.9 i/s
    Kelvin&#39;s AdvSort:     4935.3 i/s - 22.08x  slower
      Yunus&#39;s Method:     4794.6 i/s - 22.72x  slower
     Kelvin&#39;s Method:     1216.6 i/s - 89.55x  slower
</code></pre>
<p>Interesting, right? After such tests, my team and I wouldn&#39;t look at the code the same way as before, we now understand there is no absolute best code, some code work better with one situation, some work better with another situation. </p>
<p>There are many more examples to show how we dig deeper into the code to learn more, the ability to see the performances of our code help us keep doing that. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1563365381068/HWWDA0Sub.png" alt="Screen Shot 2019-07-17 at 8.05.37 PM.png"></p>
<p>I guess that is also why most of the coding challenge platform such as <a target='_blank' rel='noopener noreferrer'  href="https://leetcode.com">LeetCode</a> want to show you the performance after you finish it.</p>
<h2 id="finally-i-think-it-will-matter-at-work-">Finally, I think it will matter at work.</h2>
<p>When solving a small problem such as coding challenges, even we can see the difference from a performance test, but that comparison might be just 0.001 seconds vs 0.01 seconds, nobody can really tell and it has very little effect in the real world.</p>
<p>But all it is because coding challenges usually only employ very small dataset to prove the concepts or explain the algorithm easily. The story will be totally different once start uses bigger datasets. </p>
<pre><code class="lang-shell"># Benchmark with 1,000 size array
Comparison:
           Ruby Sort:     6454.5 i/s
    Kelvin&#39;s AdvSort:      336.0 i/s - 19.21x  slower
      Yunus&#39;s Method:       45.5 i/s - 141.97x  slower
     Kelvin&#39;s Method:       20.1 i/s - 321.18x  slower
</code></pre>
<pre><code class="lang-shell"># Benchmark with 10,000 size array
Comparison:
           Ruby Sort:      477.6 i/s
    Kelvin&#39;s AdvSort:       27.4 i/s - 17.40x  slower
      Yunus&#39;s Method:        0.7 i/s - 681.21x  slower
     Kelvin&#39;s Method:        0.4 i/s - 1254.91x  slower
</code></pre>
<p>The datasets sizes are 1,000 and 10,000 respectively, one of my methods can&#39;t even finish in 3 seconds (I pre-set as 3 seconds this benchmark test). </p>
<p>Now I can really feel the difference, it is 5 Seconds. vs 0.005 Seconds, If I put it into my web application, my web app might barely work once my products categories close to 10k. </p>
<p>My first code wouldn&#39;t work in a production system like that, my second code (with advance sort method apply) might work better, but still far from the Ruby baseline code. That is the difference between amateur and professional, but I would never know it if I don&#39;t do the performance test.</p>
<p>It also reminds me why some programmers being paid 10 or even 100 times salary because they can write more efficient code, help company&#39;s website run 1,000 or 100,000 times faster with millions to billions of data. </p>
<p>So if I keep checking the performance of my code, getting to know what works better what works worse, learn the trick along the way of #LeanToCode, I might have chance to become one of those programmers. (always have hope!)   </p>
<p>Finally, maybe I am just too optimistic, but check performance or do benchmark testing still a very good practice for either learning code or write code for work, not to mention it makes the journey more fun and interesting, agree?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1563377756362/-rXkmaIPW.jpeg" alt="rob-wingate-IlUqSRJYp8c-unsplash.jpg">
<em>Photo by Rob Wingate on Unsplash</em></p>
<h2 id="at-the-end-i-will-show-you-how-">At the end, I will show you how.</h2>
<p>The <a target='_blank' rel='noopener noreferrer'  href="https://github.com/evanphx/benchmark-ips">benchmark tools</a> I use in this article is from <a target='_blank' rel='noopener noreferrer'  href="https://github.com/evanphx">Evan Phoenix</a> for #Ruby languages only.  There might be similar tools in other languages as well, you can always find them through google. </p>
<p>First, you need to install the package for ruby.</p>
<pre><code class="lang-bash">$  gem install benchmark-ips
</code></pre>
<p>Second, import it to the file you want to test. </p>
<pre><code class="lang-ruby"><span class="hljs-comment"># Find-the-median.rb</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'benchmark/ips'</span>
...
</code></pre>
<p>Finally, add benchmark test code at the end of the file.</p>
<pre><code class="lang-ruby">...
<span class="hljs-comment"># all the methods are in the same file.</span>
<span class="hljs-comment"># also for all the test datasets. </span>
Benchmark.ips <span class="hljs-keyword">do</span> |x|
    x.config(<span class="hljs-symbol">:time</span> =&gt; <span class="hljs-number">3</span>, <span class="hljs-symbol">:warmup</span> =&gt; <span class="hljs-number">2</span>)

    x.report(<span class="hljs-string">"Kelvin's Method"</span>) {median_k(list)}           
    x.report(<span class="hljs-string">"Kelvin's AdvSort"</span>) {median_k1(list)}
    x.report(<span class="hljs-string">"Yunus's Method"</span>) {median_y(list)}
    x.report(<span class="hljs-string">"Ruby Sort"</span>) {median_r(list)}

    x.compare!    
  <span class="hljs-keyword">end</span>
</code></pre>
<p>You can find the full set of code <a target='_blank' rel='noopener noreferrer'  href="https://github.com/kelvin8773/data-algorithm/blob/master/6-sorting-challenges/ex8.7-the-median.rb">here</a>.</p>
<p>There might be a better way to test, and a different way to test in a production environment, but the point of this article is telling how important performance test is for both our study and work. </p>
<p>I hope this article is helpful for you, I would love to hear any comment and ideas from you, and I wish all of you - <strong>Happy Coding! </strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1563377954055/MSPLDEppv.png" alt="Banners - editable for students.png"></p>
]]></content:encoded></item><item><title><![CDATA[How much Bootstrap 4 Can help for a real world project?]]></title><description><![CDATA[I am a junior web developer recently learned Bootstrap 4 and try to explore the possibility of the framework in a real world frontend project.

Before Start ...

To tell the possibility, I decided to do a real-world project with Bootstrap 4 to get th...]]></description><link>https://blog.kelvinliang.cn/how-much-bootstrap-4-can-help-for-a-real-world-project</link><guid isPermaLink="true">https://blog.kelvinliang.cn/how-much-bootstrap-4-can-help-for-a-real-world-project</guid><category><![CDATA[bootstrap 4]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Sass]]></category><category><![CDATA[project]]></category><dc:creator><![CDATA[Kelvin Liang]]></dc:creator><pubDate>Mon, 24 Jun 2019 18:22:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1561353205489/CyWxmP1Ap.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>I am a junior web developer recently learned Bootstrap 4 and try to explore the possibility of the framework in a real world frontend project.</p>
</blockquote>
<h2 id="before-start-">Before Start ...</h2>
<ul>
<li>To tell the possibility, <strong>I decided to do a real-world project with Bootstrap 4 to get the experiences</strong>.</li>
<li><strong>I choose to clone the website of an Online Coding School ( <a target='_blank' rel='noopener noreferrer'  href="https://www.microverse.org/">Microverse</a><a target='_blank' rel='noopener noreferrer'  href="http://www.microverse.org">.org</a> )</strong> I am attending to  for this project, because:<ul>
<li>It is a <a target='_blank' rel='noopener noreferrer'  href="https://en.wikipedia.org/wiki/Single-page_application">Single Page Application</a> which is common for today&#39;s job market.</li>
<li>It has about 7 ~ 8 sections to introduce its features and services which make it complicated enough compared to other real-world projects.</li>
<li>It uses #Bootstrap4 and custom CSS for its fronted decoration, which is exactly what I am looking for.</li>
</ul>
</li>
<li>I didn&#39;t build the website from scratch, but I tried not to get any help from the school or any other people to <strong>ensure that is how I will work in the real world project</strong>.</li>
<li><strong>I finished this project in about</strong> a month, but I was doing few others projects and studying during the period, so the total hours I spent on this project are about <strong>30 ~ 40 hours</strong>.</li>
<li><strong>The final project</strong> is not completely identical to the original one (and I am not intend to), you <strong>can see it from this <a target='_blank' rel='noopener noreferrer'  href="https://kelvin8773.github.io/clone-microverse/">link</a>.</strong></li>
</ul>
<p>Below are how I have accomplished this project and what conclusion I came out of it. </p>
<h2 id="let-s-get-it-start-first-part-project-setup">Let&#39;s get it start =&gt; First Part - Project Setup</h2>
<p>At first, I go to <a target='_blank' rel='noopener noreferrer'  href="https://getbootstrap.com/docs/4.3/getting-started/download/">GetBootstrap</a><a target='_blank' rel='noopener noreferrer'  href="http://getbootstrap.com">.com</a> to download the latest version of <a target='_blank' rel='noopener noreferrer'  href="https://github.com/twbs/bootstrap/archive/v4.3.1.zip">Bootstrap Source Files</a>. (It is V.4.2.1), unzip it, and I saw the following files inside the folder.  (There are many sub-folders and files I have omitted). </p>
<pre><code><span class="hljs-selector-tag">bootstrap-4</span><span class="hljs-selector-class">.2</span><span class="hljs-selector-class">.1</span> <span class="hljs-selector-tag">tree</span> <span class="hljs-selector-tag">-L</span> 2
    .
    ├── <span class="hljs-selector-tag">CNAME</span>
    ├── <span class="hljs-selector-tag">CODE_OF_CONDUCT</span><span class="hljs-selector-class">.md</span>
    ├── ...
    ├── <span class="hljs-selector-tag">build</span>
    │   ├── <span class="hljs-selector-tag">banner</span><span class="hljs-selector-class">.js</span>
    │   ├── ...
    │   └── <span class="hljs-selector-tag">vnu-jar</span><span class="hljs-selector-class">.js</span>
    ├── <span class="hljs-selector-tag">composer</span><span class="hljs-selector-class">.json</span>
    ├── <span class="hljs-selector-tag">dist</span>
    │   ├── <span class="hljs-selector-tag">css</span>
    │   └── <span class="hljs-selector-tag">js</span>
    ├── <span class="hljs-selector-tag">js</span>
    │   ├── <span class="hljs-selector-tag">dist</span>
    │   ├── <span class="hljs-selector-tag">src</span>
    │   └── <span class="hljs-selector-tag">tests</span>
    ├── <span class="hljs-selector-tag">nuget</span>
    ├── <span class="hljs-selector-tag">package-lock</span><span class="hljs-selector-class">.json</span>
    ├── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.js</span>
    ├── <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span>
    ├── <span class="hljs-selector-tag">scss</span>
    │   ├── _<span class="hljs-selector-tag">alert</span><span class="hljs-selector-class">.scss</span>
    │   ├── _<span class="hljs-selector-tag">badge</span><span class="hljs-selector-class">.scss</span>
    │   ├── _<span class="hljs-selector-tag">breadcrumb</span><span class="hljs-selector-class">.scss</span>
    │   ├── _<span class="hljs-selector-tag">button-group</span><span class="hljs-selector-class">.scss</span>
    │   ├──  ...
    └── <span class="hljs-selector-tag">site</span>
        ├── _<span class="hljs-selector-tag">data</span>
        ├──  ...
        └── <span class="hljs-selector-tag">sw</span><span class="hljs-selector-class">.js</span>

    17 <span class="hljs-selector-tag">directories</span>, 67 <span class="hljs-selector-tag">files</span>
</code></pre><p>For my purpose, I only focus on one major folder - <code>./scss</code>, because I will compile my own version of CSS through <strong>Sass</strong>. I copied the entire <code>./scss</code> folder to my project folder, below are my project folder structure.</p>
<pre><code>➜  <span class="hljs-selector-tag">clone-microverse</span> <span class="hljs-selector-tag">git</span><span class="hljs-selector-pseudo">:(development)</span> <span class="hljs-selector-tag">tree</span>
.
├── <span class="hljs-selector-attr">[1.0K]</span>  <span class="hljs-selector-tag">LICENSE</span>
├── <span class="hljs-selector-attr">[ 736]</span>  <span class="hljs-selector-tag">README</span><span class="hljs-selector-class">.md</span>
├── <span class="hljs-selector-attr">[ 224]</span>  <span class="hljs-selector-tag">assets</span>
│   ├── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">bootstrap</span>
│   │   └── <span class="hljs-selector-attr">[1.4K]</span>  <span class="hljs-selector-tag">scss</span>
│   │       ├── <span class="hljs-selector-attr">[1.1K]</span>  _<span class="hljs-selector-tag">alert</span><span class="hljs-selector-class">.scss</span>
│   │       ├── <span class="hljs-selector-attr">[1.0K]</span>  _<span class="hljs-selector-tag">badge</span><span class="hljs-selector-class">.scss</span>
│   │       ├── <span class="hljs-selector-attr">[1.2K]</span>   ...
│   │       ├── <span class="hljs-selector-attr">[ 920]</span>  <span class="hljs-selector-tag">bootstrap</span><span class="hljs-selector-class">.scss</span>
│   │       ├── <span class="hljs-selector-attr">[1.0K]</span>  <span class="hljs-selector-tag">mixins</span>
│   │       └── <span class="hljs-selector-attr">[ 576]</span>  <span class="hljs-selector-tag">utilities</span>
│   ├── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">css</span>
│   │   ├── <span class="hljs-selector-attr">[438K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.css</span>
│   │   └── <span class="hljs-selector-attr">[452K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.map</span>
│   ├── <span class="hljs-selector-attr">[2.0K]</span>  <span class="hljs-selector-tag">images</span>
│   │   ├── <span class="hljs-selector-attr">[9.8K]</span>  1<span class="hljs-selector-tag">-r-kn-gdqsl-5-lie-3-l-r-7-j-n-0-a-zq-2-x</span><span class="hljs-selector-class">.png</span>
│   │   ├── <span class="hljs-selector-attr">[9.5K]</span>  2000<span class="hljs-selector-tag">-px-stack-overflow-logo-svg</span><span class="hljs-selector-class">.png</span>
│   │   ├── ...
│   └── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">scss</span>
│       ├── <span class="hljs-selector-attr">[ 27K]</span>  _<span class="hljs-selector-tag">style</span><span class="hljs-selector-class">.scss</span>
│       └── <span class="hljs-selector-attr">[1.2K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.scss</span>
└── <span class="hljs-selector-attr">[ 48K]</span>  <span class="hljs-selector-tag">index</span><span class="hljs-selector-class">.html</span>
7 <span class="hljs-selector-tag">directories</span>, 47 <span class="hljs-selector-tag">files</span>
</code></pre><p>There are 2 <code>/scss</code> folders in this project, one is <code>./asset/bootstrap/scss</code> (from Bootstrap source files) and another one is <code>./asset/scss</code> (for me to write customized code inside), and the output folder is <code>./asset/css</code> (Where my HTML will refer link to).</p>
<p>For <code>Sass</code> compiler, I use <a target='_blank' rel='noopener noreferrer'  href="https://scout-app.io/"><strong>Scout-App</strong></a>  to help, it is a Sass GUI so it is very straightforward and easy to set up.<br><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1561352901857/k743a0U4U.png" alt="Screen Shot 2019-06-24 at 1.07.56 PM.png">
 Once I finish the project setup, I start my coding in HTML &amp; Sass files. </p>
<h2 id="second-part-coding-the-hardest-part-">Second Part =&gt; Coding ... (the hardest part)</h2>
<p>Base on Bootstrap Official <a target='_blank' rel='noopener noreferrer'  href="https://getbootstrap.com/docs/4.1/getting-started/theming/">Documentation</a>,  I created a custom.scss file to add my customized elements and bootstrap base file together. </p>
<pre><code><span class="hljs-comment">// Custom.scss</span>
<span class="hljs-comment">// Option A: Include all of Bootstrap</span>

<span class="hljs-comment">/* -------begin customization-------- */</span>

$theme-colors: (primary: <span class="hljs-meta">#6f23ff,</span>
secondary: <span class="hljs-meta">#ffd540);</span>

:root {
  --dusk: <span class="hljs-meta">#3e396b;</span>
  --very-light-blue: <span class="hljs-meta">#f8faff;</span>
  --purplish-blue: rgb(<span class="hljs-number">111</span>, <span class="hljs-number">35</span>, <span class="hljs-number">255</span>);
  --tealish: rgb(<span class="hljs-number">65</span>, <span class="hljs-number">211</span>, <span class="hljs-number">189</span>);
  --light-tan: <span class="hljs-meta">#f9f1ae;</span>

  --padding-sm: calc((<span class="hljs-number">100</span>vw - <span class="hljs-number">540</span>px) / <span class="hljs-number">2</span>);
  --padding-md: calc((<span class="hljs-number">100</span>vw - <span class="hljs-number">720</span>px) / <span class="hljs-number">2</span>);
  --padding-lg: calc((<span class="hljs-number">100</span>vw - <span class="hljs-number">960</span>px) / <span class="hljs-number">2</span>);
  --padding-xl: calc((<span class="hljs-number">100</span>vw - <span class="hljs-number">1140</span>px) / <span class="hljs-number">2</span>);

  --oPadding: calc(<span class="hljs-number">0.16</span> * <span class="hljs-number">100</span>vw);
  --oHeight: calc(<span class="hljs-number">0.16</span> * (<span class="hljs-number">100</span>vw - <span class="hljs-keyword">var</span>(--padding-xl)));
  --oHalfHeight: calc(<span class="hljs-number">0.16</span> * (<span class="hljs-number">50</span>vw -<span class="hljs-keyword">var</span>(--padding-xl)));
}

<span class="hljs-comment">/* -------end customization-------- */</span>

<span class="hljs-comment">/* import Bootstrap to set the changes! */</span>

@import <span class="hljs-string">"../bootstrap/scss/bootstrap.scss"</span>;

<span class="hljs-comment">/* ----------- custom font import  ------------ */</span>

@<span class="hljs-function">import <span class="hljs-title">url</span>(<span class="hljs-params"><span class="hljs-string">"https://fonts.googleapis.com/css?family=Domine:400,700|Maven+Pro:400,500,700,900|Montserrat:400,500,600,700,900&amp;display=swap"</span></span>)</span>;

<span class="hljs-comment">/* ------------ Self-styling -------------- */</span>

@import <span class="hljs-string">"./style"</span>;
</code></pre><p>I used &quot;Option A&quot; - to include all the bootstrap components in this project, put all self-styling customization into a separated file <code>./style.scss</code>, Sass will compile all sass files into one big giant css output to <code>./asset/css/custom.css</code>, which I 
refer it back in my <code>index.html</code> file.</p>
<pre><code><span class="hljs-meta">&lt;!DOCTYPE html&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"ie=edge"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./assets/css/custom.css"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>clone-Microverse - Online Coding School<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre><p>So far all the steps above are very clear and easy to follow ( I finish them in about an hour), but what coming in next are out of my calculation. </p>
<p>I spent the first few hours trying to re-create the navigation bar and landing page by myself. The more I tried, the more I found it is hard and complex. I tried to utilize more bootstrap 4 classes and less customize CSS, but it seemed impossible.</p>
<p>The original website looks pretty slick and stylish, isn&#39;t it?  Yes, but the price is very specified and complex frontend code.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1561385050973/GwErLcdkx.png" alt="Screen Shot 2019-06-24 at 10.03.44 PM.png"></p>
<p>I finally gave it up and try to look at the original website&#39;s code to see how they actually come together, the result is shocking - there are more stacks or layers to make up one component than I originally thought, there are more custom CSS than use Bootstrap 4 class directly,  they intend to use more #id rather than class for specificity of CSS style.  Below are a peek on the code - </p>
<pre><code><span class="hljs-meta"># Index.html</span>
&lt;div id=<span class="hljs-string">"homepage"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"row bg-primary"</span>&gt;
        &lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"content"</span>&gt;
          &lt;div id=<span class="hljs-string">"homepage-navbar-container"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"container-fluid p-0"</span>&gt;
            &lt;nav id=<span class="hljs-string">"homepage-navbar"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"navbar navbar-expand-lg w-100"</span>&gt;
              &lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"container"</span>&gt;
                &lt;a href=<span class="hljs-string">"#"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"navbar-band logo"</span>&gt;microverse&lt;/a&gt;
                 ....
                &lt;div id=<span class="hljs-string">"navbarToggler"</span>
                  <span class="hljs-keyword">class</span>=<span class="hljs-string">"collapse navbar-collapse w-100 align-items-baseline"</span>&gt;
                  &lt;ul <span class="hljs-keyword">class</span>=<span class="hljs-string">"nav navbar-nav w-100 justify-content-end"</span>&gt;
                    &lt;li <span class="hljs-keyword">class</span>=<span class="hljs-string">"nav-item pr-3"</span>&gt;
                      &lt;a
                        <span class="hljs-keyword">class</span>=<span class="hljs-string">"nav-link font-weight-bold text-white text-capitalize"</span>
                        href=<span class="hljs-string">"#"</span>
                        &gt;curriculum&lt;/a
                      &gt;
                    &lt;/li&gt;
</code></pre><pre><code class="lang-scss">\\ _style.scss
#homepage-header {
    margin-top: 163px;
    position: relative;
    z-index: 105;

    .head-line {
      font-family: &quot;Domine&quot;, serif;
      font-size: 3rem;
      font-weight: bold;
      line-height: 1.27;
    }

    .head-text {
      font-size: 1.25rem;
      line-height: 1.65;
      margin-top: 15px;
      margin-right: 30px;
      color: #fff;
    }
</code></pre>
<p>Because of these complexities, I spent almost one month to finish all the coding, there are some on and off time and I just do this project on my spare time, but it still overly difficult than most of the frontend project I have done before. The final finished <code>index.html</code> file has <code>1204</code> lines, <code>_style.scss</code> file has <code>1512</code> lines, </p>
<pre><code>├── <span class="hljs-selector-attr">[1.0K]</span>  <span class="hljs-selector-tag">LICENSE</span>
├── <span class="hljs-selector-attr">[ 736]</span>  <span class="hljs-selector-tag">README</span><span class="hljs-selector-class">.md</span>
├── <span class="hljs-selector-attr">[ 224]</span>  <span class="hljs-selector-tag">assets</span>
│   ├── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">bootstrap</span>
│   │   └── <span class="hljs-selector-attr">[1.4K]</span>  <span class="hljs-selector-tag">scss</span>
│   ├── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">css</span>
│   │   ├── <span class="hljs-selector-attr">[437K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.css</span>
│   │   └── <span class="hljs-selector-attr">[451K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.map</span>
│   └── <span class="hljs-selector-attr">[ 128]</span>  <span class="hljs-selector-tag">scss</span>
│       ├── <span class="hljs-selector-attr">[ 27K]</span>  _<span class="hljs-selector-tag">style</span><span class="hljs-selector-class">.scss</span>
│       └── <span class="hljs-selector-attr">[1.1K]</span>  <span class="hljs-selector-tag">custom</span><span class="hljs-selector-class">.scss</span>
└── <span class="hljs-selector-attr">[ 48K]</span>  <span class="hljs-selector-tag">index</span><span class="hljs-selector-class">.html</span>
</code></pre><p>Finally, You can check out all the code in <a target='_blank' rel='noopener noreferrer'  href="https://github.com/kelvin8773/clone-microverse">here</a>. I have clone about 95% of the code from the original website, but it still took me many hours to accomplish it. I can&#39;t image how much hours I have to spend if I need to do this project from scratch. </p>
<h2 id="lesson-learn-conclusion-">Lesson Learn &amp; Conclusion ...</h2>
<p> At first, after finishing this project, I think I can answer the question I raise at the beginning now - <strong>How much Bootstrap 4 Can help for a real-world project?</strong></p>
<ul>
<li>A short answer  is - &quot;Not much&quot; </li>
<li>A longer answer is - &quot;It help on its grid system and some spacing utilities, but the project still needs a lot of customizing CSS to help make the website look unique and outstanding.&quot;</li>
<li>An even longer answer is - &quot;Bootstrap framework help provide a solid foundation for the project, it contributed about 20 ~ 30 % of the code base, but a real-world project needs more unique design and differentiation, imaginations and creativities are needed as a Frontend Developer!&quot;</li>
</ul>
<p>Second, below are some lesson learned I got from this project - </p>
<ul>
<li>A Real-world FrontEnd Project is usually complex and challenging. (It will be much more complex than the projects in Odin project or any other online learning platforms). </li>
<li>A Frontend framework such as Bootstrap 4 might help, but diligentness and creativities are still the main contributors.</li>
<li>Frontend development mainly involves repetitive coding and being requested to update frequently, make sure I like this kind of working style before I jump in.</li>
<li>There are still much more to learn about SASS, CSS &amp; HTML.</li>
</ul>
<p>Finally, due to the limitation of my knowledge and resources, I might do this project in a completely wrong way or an immature way, hence I got a completely wrong answer from it. But so what?  I did it and write about it anyway, I definitely learn a lot from it. </p>
<p>There are free resource can help customize Bootstrap 4 theme too, but they seem no fit for this project, so I might try them in the next project.</p>
<ul>
<li><a target='_blank' rel='noopener noreferrer'  href="https://themestr.app">themestr.app</a> </li>
<li><a target='_blank' rel='noopener noreferrer'  href="https://themesguide.github.io/top-hat/dist">Top-Hat</a></li>
</ul>
<p>I will do it again if there is a question I can&#39;t get the answer from google or any other sources, I think that is how a self-taught programmer learn to evolve in today&#39;s environment. </p>
<p>PS: I am open to your critiques and suggestion for this project, thanks again for you to finish this reading.  </p>
<p><a target='_blank' rel='noopener noreferrer'  href="https://www.microverse.org/"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1561472654832/cuH9t2DPr.png" alt="Banners - editable for students.png"></a></p>
]]></content:encoded></item></channel></rss>