<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>E2e on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/e2e/</link>
    <description>Recent content in E2e on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 25 Dec 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/e2e/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>E2E tests with Gitlab CI services</title>
      <link>https://haseebmajid.dev/posts/2021-12-25-e2e-tests-with-gitlab-ci-services/</link>
      <pubDate>Sat, 25 Dec 2021 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2021-12-25-e2e-tests-with-gitlab-ci-services/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;This will be a slightly shorter article. In this article I will show you how I&amp;rsquo;ve managed to do some
end-to-end testing with Gitlab CI services.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m building a browser-based multiplayer game called Banter Bus. Banter Bus consists of three main components,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gui: A SvelteKit based frontend the user will interact with to play the game&lt;/li&gt;
&lt;li&gt;core-api: A Socketio API written in Python&lt;/li&gt;
&lt;li&gt;management-api: A simple RESTful API written in Python (FastAPI)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now say I want to write some e2e Cypress tests, that will test all of these components interacting with each other.
Which mainly will look something like &lt;code&gt;gui -&amp;gt; core-api -&amp;gt; management-api&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>This will be a slightly shorter article. In this article I will show you how I&rsquo;ve managed to do some
end-to-end testing with Gitlab CI services.</p>
<p>I&rsquo;m building a browser-based multiplayer game called Banter Bus. Banter Bus consists of three main components,</p>
<ul>
<li>gui: A SvelteKit based frontend the user will interact with to play the game</li>
<li>core-api: A Socketio API written in Python</li>
<li>management-api: A simple RESTful API written in Python (FastAPI)</li>
</ul>
<p>Now say I want to write some e2e Cypress tests, that will test all of these components interacting with each other.
Which mainly will look something like <code>gui -&gt; core-api -&gt; management-api</code>.</p>
<p>Each of these project deploys its own Docker container, which we can then use for testing it. So how can we do this with Gitlab CI ?</p>
<h2 id="gitlab-services">Gitlab Services</h2>
<p>What is a Gitlab CI service ?</p>
<blockquote>
<p>The services keyword defines a Docker image that runs during a job linked to the Docker image that the image keyword defines. This allows you to access the service image during build time. - <a href="https://docs.gitlab.com/ee/ci/services/">https://docs.gitlab.com/ee/ci/services/</a></p>
</blockquote>
<p>Essentially they are Docker containers we can use in our CI jobs.</p>
<h2 id="packagejson">package.json</h2>
<p>For the examples below assume our <code>package.json</code> scipts section looks something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;svelte-kit dev&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;e2e&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress run --browser chrome&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;e2e:ci&#34;</span><span class="p">:</span> <span class="s2">&#34;start-server-and-test dev http://localhost:3000 e2e&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="gitlab-ci">Gitlab CI</h2>
<p>Let&rsquo;s take a look at an example <code>.gitlab-ci.yml</code> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">cypress-e2e-chrome</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">cypress/browsers:node14.17.0-chrome88-ff89</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">BANTER_BUS_CORE_API_MANAGEMENT_API_URL</span><span class="p">:</span><span class="w"> </span><span class="l">http://banter-bus-management-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">BANTER_BUS_CORE_API_DB_HOST</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nt">FF_NETWORK_PER_BUILD</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c"># Hidden the rest of the variables as not to clutter the file</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mongo:4.4.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-core-api:test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-core-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-management-api:test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-management-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-management-api/database-seed:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database-seed</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span>- <span class="l">npm ci</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">export VITE_BANTER_BUS_CORE_API_URL=http://banter-bus-core-api:8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf &amp;&amp; sysctl -p</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">npm run e2e:ci</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">artifacts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expire_in</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="l">week</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">cypress/screenshots</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">cypress/videos</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">reports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">junit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">results/TEST-*.xml</span><span class="w">
</span></span></span></code></pre></div><h3 id="services">Services</h3>
<p>Let&rsquo;s break file down a bit, these are essentially all the dependencies of our <code>gui</code> application. We need all of
these containers running.</p>
<p>In this case we need four containers (this doesn&rsquo;t really matter):</p>
<ul>
<li>banter-bus-database: A database for the core-api and management-api</li>
<li>banter-bus-core-api: The main API the gui will interact with</li>
<li>banter-bus-management-api: Used to help manage our available games, questions etc</li>
<li>banter-bus-database-seed: A short lived container which pre-fills the database with some values</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mongo:4.4.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-core-api:test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-core-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-management-api:test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-management-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">registry.gitlab.com/banter-bus/banter-bus-management-api/database-seed:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">alias</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database-seed</span><span class="w">
</span></span></span></code></pre></div><p>In our examples the <code>name</code> field is the image name, this is the same name you&rsquo;d use when using the <code>docker pull</code>
command. The next field is <code>alias</code> this is the name we&rsquo;ll use to reference that container. This is the container name.</p>
<p>To see how the <code>alias</code> is used, is to look at the environment variables we have provided for the job
<code>BANTER_BUS_CORE_API_DB_HOST: banter-bus-database</code>. So core-api will try to connect to database using
this host. You can read more about Docker is able to resolve this to an <a href="/blog/dns-docker-explained/">IP address here</a>. Another example is how the URL core-api will use to connect to the management-api
<code>BANTER_BUS_CORE_API_MANAGEMENT_API_URL: http://banter-bus-management-api</code>.</p>
<details
  class="notice danger"
  open="true"
>
    <summary class="notice-title">ENV Variable</summary>
  
  One environment variable we must provide is <code>FF_NETWORK_PER_BUILD</code> set to <code>1</code> (or true). Docker then
creates a bridge network so all the services can communicate amognst themselves. You can read more about
<a href="https://docs.gitlab.com/runner/executors/docker.html#create-a-network-for-each-job">it here</a>
</details>

<p>We&rsquo;ve discussed the most important part of the CI file, but lets quickly discuss the rest for completeness</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Optional</summary>
  
  I&rsquo;ve discussed the main point of this article, how to use services and how to get them to work together.
</details>

<h3 id="variables">Variables</h3>
<p>We&rsquo;ve already spoken about this above, but lets take a quick look at the <code>variables</code> section.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">BANTER_BUS_CORE_API_MANAGEMENT_API_URL</span><span class="p">:</span><span class="w"> </span><span class="l">http://banter-bus-management-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">BANTER_BUS_CORE_API_DB_HOST</span><span class="p">:</span><span class="w"> </span><span class="l">banter-bus-database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">FF_NETWORK_PER_BUILD</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Hidden the rest of the variables as not to clutter the f</span><span class="w">
</span></span></span></code></pre></div><p>These are environment variables that are shared both with the job and the services. Some of these are
config passed to the application, such as <code>BANTER_BUS_CORE_API_MANAGEMENT_API_URL</code> and <code>BANTER_BUS_CORE_API_DB_HOST</code>.</p>
<h3 id="script">Script</h3>
<p>Since we are using the <code>cypress/browsers:node14.17.0-chrome88-ff89</code> image, we have access to chrome
(headless) browser we can use with Cypress.</p>
<p>So we can do something like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">npm ci</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">export VITE_BANTER_BUS_CORE_API_URL=http://banter-bus-core-api:8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">npm run e2e:ci</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>npm ci</code>: Installs our npm dependencies for the gui app</li>
<li><code>export VITE_BANTER_BUS_CORE_API_URL=http://banter-bus-core-api:8080</code> exports an enviroment variable which will be used by the gui app so it knows the URL of the core-api. Note the use of the alias name here (and port <code>:8080</code> default port for the core-api)</li>
<li><code>npm run e2e:ci</code>: Starts the dev server and then runs the cypress test, see <code>start-server-and-test dev http://localhost:3000 e2e</code> where <code>e2e</code> is <code>cypress run --browser chrome</code></li>
</ul>
<h3 id="artifacts">Artifacts</h3>
<p>Finally, the artifacts are &ldquo;things&rdquo; that are left over after the build. In this case we use them in two ways:</p>
<ul>
<li>One to generate a coverage report with <code>junit</code></li>
<li>Two to save our Cypress screenshots and videos</li>
</ul>
<p>The downloadable artifacts will expire after 1 week. The Cypress files can be useful when debugging a problem
with your tests. You get a video of perhaps why the tests failed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">artifacts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">expire_in</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="l">week</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">cypress/screenshots</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">cypress/videos</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">reports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">junit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">results/TEST-*.xml</span><span class="w">
</span></span></span></code></pre></div><h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/banter-bus/banter-bus-gui/-/tree/350f1f986b077ac86da924b830fed88ffcd3cde0">Example Project</a></li>
<li><a href="https://gitlab.com/banter-bus/banter-bus-gui/-/jobs/1920396599">Example Job</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
