<?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>Testing on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/testing/</link>
    <description>Recent content in Testing on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Thu, 16 Nov 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/testing/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>TIL: How to Debug a Test in Golang With Build Tags in Neovim</title>
      <link>https://haseebmajid.dev/posts/2023-11-16-til-how-to-debug-a-test-in-golang-with-build-tags-in-neovim/</link>
      <pubDate>Thu, 16 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-11-16-til-how-to-debug-a-test-in-golang-with-build-tags-in-neovim/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TIL: How to Debug a Test in Golang With Build Tags in Neovim&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I was having issues with my debugger today (well technically yesterday because I am publishing this a day later to spread
out my blog posts but same difference) and it took me a few hours to realise what was going on. In my case, I was
trying to debug a test written in Golang using &lt;code&gt;nvim-dap-go&lt;/code&gt; on Neovim.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>TIL: How to Debug a Test in Golang With Build Tags in Neovim</strong></p>
<p>I was having issues with my debugger today (well technically yesterday because I am publishing this a day later to spread
out my blog posts but same difference) and it took me a few hours to realise what was going on. In my case, I was
trying to debug a test written in Golang using <code>nvim-dap-go</code> on Neovim.</p>
<p>The reason the test was failing  because the test file had build tags so delve (the debugger) couldn&rsquo;t compile
the file, i.e. using the unit build tag in the example below.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//go:build unit
</span></span></span><span class="line"><span class="cl"><span class="c1">// +build unit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">options_test</span>
</span></span></code></pre></div><p>When we set up the <code>dag-go</code> plugin, we need to pass an extra field called <code>build_flags</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">lua</span> <span class="n">require</span><span class="p">(</span><span class="s1">&#39;dap-go&#39;</span><span class="p">).</span><span class="n">setup</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">delve</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">build_flags</span> <span class="o">=</span> <span class="s2">&#34;-tags=unit,integration,e2e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Replace the above with the tags you use! If you are using NixVim you should be able to do something like this <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">programs</span><span class="o">.</span><span class="n">nixvim</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">plugins</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">dap</span><span class="o">.</span><span class="n">extensions</span><span class="o">.</span><span class="n">dap-go</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">delve</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">path</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">delve</span><span class="si">}</span><span class="s2">/bin/dlv&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">buildFlags</span> <span class="o">=</span> <span class="s2">&#34;-tags=unit,integration,e2e&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then I have a keybinding to debug the nearest test like so, again using NixVim.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">action</span> <span class="o">=</span> <span class="s2">&#34;&lt;cmd&gt; lua require(&#39;dap-go&#39;).debug_test()&lt;CR&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span> <span class="o">=</span> <span class="s2">&#34;&lt;leader&gt;td&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Debug Nearest (Go)&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">mode</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;n&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>in lua:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">vim.keymap</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="s2">&#34;n&#34;</span><span class="p">,</span> <span class="s2">&#34;&lt;leader&gt;td&#34;</span><span class="p">,</span> <span class="s2">&#34;&lt;cmd&gt;lua require(&#39;dap-go&#39;).debug_test()&lt;CR&gt;&#34;</span><span class="p">)</span>
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>After this PR is merged, <a href="https://github.com/nix-community/nixvim/pull/703">https://github.com/nix-community/nixvim/pull/703</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to Use Neotest Dap With Lazyvim for Golang Development</title>
      <link>https://haseebmajid.dev/posts/2023-07-31-how-to-use-neotest-dap-with-lazyvim-for-golang-development/</link>
      <pubDate>Mon, 31 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-07-31-how-to-use-neotest-dap-with-lazyvim-for-golang-development/</guid>
      <description>&lt;p&gt;In this blog post, I will show you how you can easily setup Neotest to make testing easier in neovim and DAP to make
debugging your tests is a lot easier.&lt;/p&gt;
&lt;p&gt;I am using the &lt;a href=&#34;https://www.lazyvim.org/&#34;&gt;LazyVim&lt;/a&gt; neovim distribution, where these two come as easily installed extra
plugins. However, it should be easy enough for you to copy the config over to your neovim lua config.
LazyVim even has the config available on its website, you don&amp;rsquo;t even need to deep dive into the code &amp;#x1f604;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this blog post, I will show you how you can easily setup Neotest to make testing easier in neovim and DAP to make
debugging your tests is a lot easier.</p>
<p>I am using the <a href="https://www.lazyvim.org/">LazyVim</a> neovim distribution, where these two come as easily installed extra
plugins. However, it should be easy enough for you to copy the config over to your neovim lua config.
LazyVim even has the config available on its website, you don&rsquo;t even need to deep dive into the code &#x1f604;.</p>
<p>For example with <a href="https://www.lazyvim.org/extras/dap/core">dap</a>. Here are some examples of what it can look like. The two
main bits we will look at today don&rsquo;t come by default with LazyVim but are &ldquo;extras&rdquo; we can add them</p>
<h2 id="neotest">Neotest</h2>
<p>To set up Neotest on LazyVim, go to your LazyVim <a href="https://github.com/LazyVim/starter">starter repo</a>. Then open our
<code>lazy.lua</code> file and add the following lines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">spec</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- add LazyVim and import its plugins</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;LazyVim/LazyVim&#34;</span><span class="p">,</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;lazyvim.plugins&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- import any extras modules here</span>
</span></span><span class="line hl"><span class="cl">    <span class="p">{</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;lazyvim.plugins.extras.test.core&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;lazyvim.plugins.extras.lang.go&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- import/override with your plugins</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;plugins&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>Here you can see the output from a test at the bottom (from Neotest) and a test summary (on the right).
Using the test summary we can easily run one test at a time, even subtests.</p>
<p><img
        loading="lazy"
        src="/posts/2023-07-31-how-to-use-neotest-dap-with-lazyvim-for-golang-development/images/test_summary.png"
        type=""
        alt="Test Summary"
        
      /></p>
<p>The config that LazyVim sets the following key binds.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">keys</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;tt&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">run.run</span><span class="p">(</span><span class="n">vim.fn</span><span class="p">.</span><span class="n">expand</span><span class="p">(</span><span class="s2">&#34;%&#34;</span><span class="p">))</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Run File&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;tT&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">run.run</span><span class="p">(</span><span class="n">vim.loop</span><span class="p">.</span><span class="n">cwd</span><span class="p">())</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Run All Test Files&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;tr&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">run.run</span><span class="p">()</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Run Nearest&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;ts&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">summary.toggle</span><span class="p">()</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Toggle Summary&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;to&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">output.open</span><span class="p">({</span> <span class="n">enter</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span> <span class="n">auto_close</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">})</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Show Output&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;tO&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">output_panel.toggle</span><span class="p">()</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Toggle Output Panel&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;&lt;leader&gt;tS&#34;</span><span class="p">,</span> <span class="kr">function</span><span class="p">()</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;neotest&#34;</span><span class="p">).</span><span class="n">run.stop</span><span class="p">()</span> <span class="kr">end</span><span class="p">,</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">&#34;Stop&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>So we can get the test summary by pressing <code>&lt;space&gt; + t + s</code>.
Or test output using <code>&lt;space&gt; + t + shift + o</code>. Where <code>&lt;space&gt;</code> is my leader key. Also if a test fails when I run
the nearest test <code>&lt;space&gt; + t + n</code>, the output panel (at the bottom) auto opens.</p>
<h2 id="dap-core">DAP Core</h2>
<p>Similar to above to add debugging support we can add an extra plugin like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">spec</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- add LazyVim and import its plugins</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="s2">&#34;LazyVim/LazyVim&#34;</span><span class="p">,</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;lazyvim.plugins&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- import any extras modules here</span>
</span></span><span class="line hl"><span class="cl">    <span class="p">{</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;lazyvim.plugins.extras.dap.core&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- import/override with your plugins</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="n">import</span> <span class="o">=</span> <span class="s2">&#34;plugins&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p><img
        loading="lazy"
        src="/posts/2023-07-31-how-to-use-neotest-dap-with-lazyvim-for-golang-development/images/test_debug.png"
        type=""
        alt="Test Debug"
        
      /></p>
<p>To debug our tests first we can add a breakpoint using <code>&lt;space&gt; + d + b</code>. Then we can run our test in debug mode using
<code>&lt;space&gt; + t + d</code>, which will run the nearest test in <a href="https://github.com/LazyVim/LazyVim/blob/566049aa4a26a86219dd1ad1624f9a1bf18831b6/lua/lazyvim/plugins/extras/lang/go.lua#L20">debug mode</a>.</p>
<p>When we start to debug our tests it looks something like this. If you have used the VSCode debugger this will look very
similar. We can explore the current state, the values various variables have etc. Run commands in the debugger at the
bottom.</p>
<p>That&rsquo;s it! We can improve our testing experience in Go using neovim with Neotest and DAP. I am also able to replicate
some features I used to have in VSCode and now also have in neovim 💗.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=7Nt8n3rjfDY">This video helping me get set up by Elijah Manor (he does great videos btw)</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to Get Code Coverage From Playwright Tests in a Sveltekit App</title>
      <link>https://haseebmajid.dev/posts/2023-04-15--how-to-get-code-coverage-from-playwright-tests-in-a-sveltekit-app-/</link>
      <pubDate>Sat, 15 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-04-15--how-to-get-code-coverage-from-playwright-tests-in-a-sveltekit-app-/</guid>
      <description>&lt;p&gt;&lt;img
        loading=&#34;lazy&#34;
        src=&#34;https://haseebmajid.dev/posts/2023-04-15--how-to-get-code-coverage-from-playwright-tests-in-a-sveltekit-app-/images/code_coverage.jpg&#34;
        type=&#34;&#34;
        alt=&#34;Code Coverage Meme&#34;
        
      /&gt;&lt;/p&gt;
&lt;p&gt;In this post, I will show you how to get code coverage reports from your Playwright tests in your SvelteKit app.
So let&amp;rsquo;s imagine we are starting with the basic SvelteKit template. First, we need to install:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm i -D vite-plugin-istanbul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We need the vite plugin to instrument our code using Istanbul.
Istanbul is a tool which allows us to instrument our code such that it can determine which lines were covered
by our tests.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><img
        loading="lazy"
        src="/posts/2023-04-15--how-to-get-code-coverage-from-playwright-tests-in-a-sveltekit-app-/images/code_coverage.jpg"
        type=""
        alt="Code Coverage Meme"
        
      /></p>
<p>In this post, I will show you how to get code coverage reports from your Playwright tests in your SvelteKit app.
So let&rsquo;s imagine we are starting with the basic SvelteKit template. First, we need to install:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm i -D vite-plugin-istanbul
</span></span></code></pre></div><p>We need the vite plugin to instrument our code using Istanbul.
Istanbul is a tool which allows us to instrument our code such that it can determine which lines were covered
by our tests.</p>
<h2 id="viteconfigts">vite.config.ts</h2>
<p>First, let&rsquo;s update our <code>vite.config.ts</code> (or <code>.js</code>) file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">sveltekit</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@sveltejs/kit/vite&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">defineConfig</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;vitest/config&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">istanbul</span> <span class="kr">from</span> <span class="s2">&#34;vite-plugin-istanbul&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineConfig</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">build</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sourcemap</span>: <span class="kt">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sveltekit</span><span class="p">(),</span>
</span></span><span class="line hl"><span class="cl">    <span class="nx">istanbul</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">include</span><span class="o">:</span> <span class="s2">&#34;src/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">exclude</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;node_modules&#34;</span><span class="p">,</span> <span class="s2">&#34;test/&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">extension</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;.ts&#34;</span><span class="p">,</span> <span class="s2">&#34;.svelte&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">requireEnv</span>: <span class="kt">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">forceBuildInstrument</span>: <span class="kt">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}),</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">test</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">include</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;src/**/*.{test,spec}.{js,ts}&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><blockquote>
<p>forceBuildInstrument - Optional boolean to enforce the plugin to add instrumentation in build mode. Defaults to false.</p>
</blockquote>
<p>In theory, we should just be able to use <code>requireEnv: true</code> and then pass the <code>VITE_COVERAGE=true</code> environment variable.
To our playwright tests job, so instead I just set the <code>forceBuildInstrument</code>. Now, this always instrument our builds with Istanbul.
However, we can do something like <code>process.env.NODE_ENV === &quot;test&quot;</code>. Which will only run when <code>NODE_ENV</code> is <code>test</code>.
In this example, we will keep it simple and leave it as is.</p>
<h2 id="tests">tests</h2>
<p>Now let&rsquo;s go to our <code>tests</code> folder we need to add a new file called <code>baseFixtures.ts</code> [1], which looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">fs</span> <span class="kr">from</span> <span class="s1">&#39;fs&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">path</span> <span class="kr">from</span> <span class="s1">&#39;path&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="kr">as</span> <span class="nx">crypto</span> <span class="kr">from</span> <span class="s1">&#39;crypto&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">test</span> <span class="kr">as</span> <span class="nx">baseTest</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@playwright/test&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">istanbulCLIOutput</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">cwd</span><span class="p">(),</span> <span class="s1">&#39;.nyc_output&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">generateUUID</span><span class="p">()</span><span class="o">:</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">16</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">test</span> <span class="o">=</span> <span class="nx">baseTest</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">context</span>: <span class="kt">async</span> <span class="p">({</span> <span class="nx">context</span> <span class="p">},</span> <span class="nx">use</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="nx">context</span><span class="p">.</span><span class="nx">addInitScript</span><span class="p">(()</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;beforeunload&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">collectIstanbulCoverage</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__coverage__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">promises</span><span class="p">.</span><span class="nx">mkdir</span><span class="p">(</span><span class="nx">istanbulCLIOutput</span><span class="p">,</span> <span class="p">{</span> <span class="nx">recursive</span>: <span class="kt">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="nx">context</span><span class="p">.</span><span class="nx">exposeFunction</span><span class="p">(</span><span class="s1">&#39;collectIstanbulCoverage&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">coverageJSON</span>: <span class="kt">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">coverageJSON</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFileSync</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">istanbulCLIOutput</span><span class="p">,</span> <span class="sb">`playwright_coverage_</span><span class="si">${</span><span class="nx">generateUUID</span><span class="p">()</span><span class="si">}</span><span class="sb">.json`</span><span class="p">),</span> <span class="nx">coverageJSON</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="nx">use</span><span class="p">(</span><span class="nx">context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">page</span> <span class="k">of</span> <span class="nx">context</span><span class="p">.</span><span class="nx">pages</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">evaluate</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">collectIstanbulCoverage</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">((</span><span class="nb">window</span> <span class="kr">as</span> <span class="kt">any</span><span class="p">).</span><span class="nx">__coverage__</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">expect</span> <span class="o">=</span> <span class="nx">test</span><span class="p">.</span><span class="nx">expect</span><span class="p">;</span>
</span></span></code></pre></div><p>We will use <code>test</code> and <code>expect</code> functions from this module instead of the playwright ones. As this module,
will collect the corresponding coverage files into <code>.nyc_output</code> in a JSON file.</p>
<p>Now open our tests file and update them to use the base fixture module from this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">expect</span><span class="p">,</span> <span class="nx">test</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@playwright/test&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">expect</span><span class="p">,</span> <span class="nx">test</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;./baseFixtures.js&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="run-the-tests">Run the Tests</h2>
<p>Now we can run the tests like <code>npm run test</code>, this should create a new file <code>.nyc_output</code>.
To see the actual coverage we need to use <code>nyc</code> we can do that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter<span class="o">=</span>text --exclude-after-remap <span class="nb">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># output</span>
</span></span><span class="line"><span class="cl">--------------<span class="p">|</span>---------<span class="p">|</span>----------<span class="p">|</span>---------<span class="p">|</span>---------<span class="p">|</span>-------------------
</span></span><span class="line"><span class="cl">File          <span class="p">|</span> % Stmts <span class="p">|</span> % Branch <span class="p">|</span> % Funcs <span class="p">|</span> % Lines <span class="p">|</span> Uncovered Line <span class="c1">#s </span>
</span></span><span class="line"><span class="cl">--------------<span class="p">|</span>---------<span class="p">|</span>----------<span class="p">|</span>---------<span class="p">|</span>---------<span class="p">|</span>-------------------
</span></span><span class="line"><span class="cl">All files     <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>        <span class="m">0</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>                   
</span></span><span class="line"><span class="cl"> +page.svelte <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>        <span class="m">0</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span> <span class="m">16</span>                
</span></span><span class="line"><span class="cl"> +page.ts     <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>      <span class="m">100</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>     <span class="m">100</span> <span class="p">|</span>                   
</span></span><span class="line"><span class="cl">--------------<span class="p">|</span>---------<span class="p">|</span>----------<span class="p">|</span>---------<span class="p">|</span>---------<span class="p">|</span>-------------------
</span></span></code></pre></div><h3 id="c8">C8</h3>
<p>If we want to generate a C8 (Coberatura) report we can do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter<span class="o">=</span>cobertura --exclude-after-remap <span class="nb">false</span>
</span></span></code></pre></div><p>This will save the code coverage data in the C8 format in the <code>coverage</code> folder.</p>
<h3 id="junit">JUnit</h3>
<p>We can also generate a JUnit file, first we need to add the following to our <code>playwright.config.ts</code> file:
<code>reporter: [[&quot;junit&quot;, { outputFile: &quot;results.xml&quot; }]]</code>. Then to generate the report we need to run
<code>PLAYWRIGHT_JUNIT_OUTPUT_NAME=results.xml npm run test</code>, we pass an environment variable to tell playwright where to save
the JUnit file.</p>
<h2 id="optional-run-in-gitlab-ci">(Optional) Run in GitLab CI</h2>
<p>If we put all of this together we can run the tests in GitLab CI and get the code coverage, JUnit and Coberatura reports 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">image</span><span class="p">:</span><span class="w"> </span><span class="l">node</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="w"></span><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="w"></span><span class="nt">tests:e2e</span><span class="p">:</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">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">npx playwright install</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">npm run test -- --reporter=junit</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=cobertura --exclude-after-remap false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=text --exclude-after-remap false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">coverage</span><span class="p">:</span><span class="w"> </span><span class="l">/All files[^|]*\|[^|]*\s+([\d\.]+)/</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">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 class="l">results.xml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">coverage_report</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">coverage_format</span><span class="p">:</span><span class="w"> </span><span class="l">cobertura</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">coverage/cobertura-coverage.xml</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">test-results/</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></code></pre></div><p>GitLab will be able to show some useful information using this code coverage data, such as lines covered in an open MR.
It can show if the code coverage has gone up or down within the MR as well.</p>
<p>That&rsquo;s it! Hopefully, this post has helped you set up retrieving code coverage from your playwright tests!</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2023-04-15--how-to-get-code-coverage-from-playwright-tests-in-a-sveltekit-app-/example">Example source code</a></li>
<li><a href="https://github.com/microsoft/playwright/discussions/20841">Playwright Discussion</a></li>
<li><a href="https://github.com/stevez/playwright-test-coverage/tree/integrate-vite-plugin-istanbul">Inspired By this repo</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>TIL: How to Mock Classes Using Vitest</title>
      <link>https://haseebmajid.dev/posts/2023-04-01--til-mock-classes-using-vitest-/</link>
      <pubDate>Sat, 01 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-04-01--til-mock-classes-using-vitest-/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TIL: How to Mock Classes Using Vitest&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Recently I have been creating a SvelteKit app, when creating a new SvelteKit app you get a choice
of different things you can add. Such as using &lt;code&gt;vitest&lt;/code&gt; for unit testing.&lt;/p&gt;
&lt;p&gt;I needed to spy on/mock a method in a class, to see if it was called when a button was pressed and, it was called
with the correct arguments.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say we have a &lt;code&gt;Button&lt;/code&gt; component which looks like this:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>TIL: How to Mock Classes Using Vitest</strong></p>
<p>Recently I have been creating a SvelteKit app, when creating a new SvelteKit app you get a choice
of different things you can add. Such as using <code>vitest</code> for unit testing.</p>
<p>I needed to spy on/mock a method in a class, to see if it was called when a button was pressed and, it was called
with the correct arguments.</p>
<p>Let&rsquo;s say we have a <code>Button</code> component which looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-svelte" data-lang="svelte"><span class="line"><span class="cl"><span class="c">&lt;!-- button.svelte --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">import</span> <span class="p">{</span> <span class="nx">API</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;./api&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">api</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">API</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">on:click</span><span class="o">=</span><span class="p">{</span><span class="nx">api</span><span class="p">.</span><span class="nx">create</span><span class="p">}</span><span class="err">&gt;</span><span class="s">Press</span> <span class="na">Me</span><span class="err">!&lt;/</span><span class="na">button</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Then the <code>api.ts</code> looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">class</span> <span class="nx">API</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kr">async</span> <span class="nx">create() {</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ... does something
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>So how do we test that the <code>create</code> method is called?
Let&rsquo;s assume we will be using the <code>svelte testing library</code> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">// __tests__/button.test.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">render</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@testing-library/svelte&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">userEvent</span> <span class="kr">from</span> <span class="s2">&#34;@testing-library/user-event&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">describe</span><span class="p">,</span> <span class="nx">expect</span><span class="p">,</span> <span class="nx">test</span><span class="p">,</span> <span class="nx">vi</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;vitest&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Button</span> <span class="kr">from</span> <span class="s2">&#34;../Button.svelte&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">API</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;./api&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">describe</span><span class="p">(</span><span class="s2">&#34;Button&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">test</span><span class="p">(</span><span class="s2">&#34;Successfully render Button&#34;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">userEvent</span><span class="p">.</span><span class="nx">setup</span><span class="p">();</span>
</span></span><span class="line hl"><span class="cl">		<span class="kr">const</span> <span class="nx">mock</span> <span class="o">=</span> <span class="nx">vi</span><span class="p">.</span><span class="nx">spyOn</span><span class="p">(</span><span class="nx">API</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="s2">&#34;create&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="p">{</span> <span class="nx">getByRole</span><span class="p">,</span> <span class="nx">getByLabelText</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="nx">Button</span><span class="p">,</span> <span class="p">{</span> <span class="nx">props</span><span class="o">:</span> <span class="p">{}</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">button</span> <span class="o">=</span> <span class="nx">getByRole</span><span class="p">(</span><span class="s2">&#34;button&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Add Bookmark&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">user</span><span class="p">.</span><span class="nx">click</span><span class="p">(</span><span class="nx">button</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="nx">expect</span><span class="p">(</span><span class="nx">mock</span><span class="p">).</span><span class="nx">toHaveBeenCalled</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The key being this line <code>vi.spyOn(API.prototype, &quot;create&quot;)</code>, where we need to use <code>.prototype</code>.
So we can access the methods in the class. Without the <code>.prototype</code> we will not find the method
on the object itself.</p>
<p>That&rsquo;s it! Thanks for reading.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/bookmarkey/gui/-/blob/main/src/lib/components/organisms/__tests__/AddBookmarkModal.test.ts#L11">Example Test from my App</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://testing-library.com/docs/svelte-testing-library/intro/">https://testing-library.com/docs/svelte-testing-library/intro/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>TIL: How to Use Multiple Auth Files in Playwright</title>
      <link>https://haseebmajid.dev/posts/2023-03-22--til-how-to-use-multiple-auth-in-playwright-/</link>
      <pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2023-03-22--til-how-to-use-multiple-auth-in-playwright-/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TIL: How to Load Authenticate State in Playwright&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this post, I will quickly show you how you can reduce boilerplate code to log in to your app in Playwright tests.
Log in to our app is a very common action that will likely be required in most of our tests.&lt;/p&gt;
&lt;p&gt;You can read this &lt;a href=&#34;https://playwright.dev/docs/auth&#34;&gt;documentation here&lt;/a&gt;, which will explain how you can set this up.
However, what do you do if you want to say test the login flow or the register flow? You cannot use the same auth
file as all the other tests as you will already be logged in.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>TIL: How to Load Authenticate State in Playwright</strong></p>
<p>In this post, I will quickly show you how you can reduce boilerplate code to log in to your app in Playwright tests.
Log in to our app is a very common action that will likely be required in most of our tests.</p>
<p>You can read this <a href="https://playwright.dev/docs/auth">documentation here</a>, which will explain how you can set this up.
However, what do you do if you want to say test the login flow or the register flow? You cannot use the same auth
file as all the other tests as you will already be logged in.</p>
<p>It is a great way to reduce boilerplate code from our tests, which would require us to write code to log in before each test.
You could log yourself in the test but, then we are adding more boilerplate code again.</p>
<p>What you can do is create a 2nd file say called <code>playwright/.auth/not_logged_in.json</code> which looks like this:</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;cookies&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;origins&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In other words there is no state, so the user will not be logged in.
Then we can do something like this in say our <code>login.test.ts</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="kr">type</span> <span class="p">{</span> <span class="nx">Page</span><span class="p">,</span> <span class="nx">expect</span><span class="p">,</span> <span class="nx">test</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@playwright/test&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">.</span><span class="nx">describe</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">let</span> <span class="nx">page</span>: <span class="kt">Page</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">	<span class="nx">test</span><span class="p">.</span><span class="nx">beforeEach</span><span class="p">(</span><span class="kr">async</span> <span class="p">({</span> <span class="nx">browser</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">loginContext</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">newContext</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">			<span class="nx">storageState</span><span class="o">:</span> <span class="s2">&#34;playwright/.auth/not_logged_in.json&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="p">});</span>
</span></span><span class="line"><span class="cl">		<span class="nx">page</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">loginContext</span><span class="p">.</span><span class="nx">newPage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">test</span><span class="p">(</span><span class="s2">&#34;Successfully login to app&#34;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">baseURL</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s2">&#34;/login&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">email</span> <span class="o">=</span> <span class="s2">&#34;test@bookmarkey.app&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;[name=&#34;email&#34;]&#39;</span><span class="p">).</span><span class="kr">type</span><span class="p">(</span><span class="nx">email</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">password</span> <span class="o">=</span> <span class="s2">&#34;password@11&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;[name=&#34;password&#34;]&#39;</span><span class="p">).</span><span class="kr">type</span><span class="p">(</span><span class="nx">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;button[type=&#34;submit&#34;]&#39;</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">waitForURL</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/my/collections/0`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">test</span><span class="p">(</span><span class="s2">&#34;Fail to login to app using incorrect credentials&#34;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">baseURL</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s2">&#34;/login&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">email</span> <span class="o">=</span> <span class="s2">&#34;test@bookmarkey.app&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;[name=&#34;email&#34;]&#39;</span><span class="p">).</span><span class="kr">type</span><span class="p">(</span><span class="nx">email</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">password</span> <span class="o">=</span> <span class="s2">&#34;wrong_password&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;[name=&#34;password&#34;]&#39;</span><span class="p">).</span><span class="kr">type</span><span class="p">(</span><span class="nx">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s1">&#39;button[type=&#34;submit&#34;]&#39;</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kr">const</span> <span class="nx">toastMessage</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="s2">&#34;.message&#34;</span><span class="p">).</span><span class="nx">innerText</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">		<span class="nx">expect</span><span class="p">(</span><span class="nx">toastMessage</span><span class="p">).</span><span class="nx">toBe</span><span class="p">(</span><span class="s2">&#34;Wrong email and password combination.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">waitForURL</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/login`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Rather than using the &ldquo;normal&rdquo; page argument provided by Playwright, we will create our context.
Using this stateless file, will mean the user is not logged in.</p>
<p>If we still want a test to use to automatically logged in we can do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">test</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@playwright/test&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">.</span><span class="nx">describe</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">test</span><span class="p">(</span><span class="s2">&#34;Successfully load collections in side bar&#34;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">page</span><span class="p">,</span> <span class="nx">baseURL</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/my/collections/0`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">getByRole</span><span class="p">(</span><span class="s2">&#34;link&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;folder closed test&#34;</span> <span class="p">}).</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">		<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">waitForURL</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/my/collections/46lfmlwhymhv6xl`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>That&rsquo;s it!</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/bookmarkey/gui/-/blob/55f7a6456da20a362c7b7ccf6069a9118acaa7f7/tests/auth.setup.ts">Used in the Bookmarkey Project</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <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>
      
      <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="w"></span><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>
    
    <item>
      <title>Testing a socketio Web App written in Python</title>
      <link>https://haseebmajid.dev/posts/2021-12-23-testing-a-socketio-web-app-written-in-python/</link>
      <pubDate>Thu, 23 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2021-12-23-testing-a-socketio-web-app-written-in-python/</guid>
      <description>&lt;p&gt;In this article I will show you how you can test an async Socketio application in Python, where the ASGI server we are running is uvicorn.
I will be referring to these tests as integration tests, though depending on who you ask they could be called E2E tests, system tests, slow test etc.
What I am referring to is simply testing out the entire &amp;ldquo;flow&amp;rdquo; of a socketio event i.e. emitting an event from a client, then receiving it on the web service
and for my actual projects interacting with an actual database.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this article I will show you how you can test an async Socketio application in Python, where the ASGI server we are running is uvicorn.
I will be referring to these tests as integration tests, though depending on who you ask they could be called E2E tests, system tests, slow test etc.
What I am referring to is simply testing out the entire &ldquo;flow&rdquo; of a socketio event i.e. emitting an event from a client, then receiving it on the web service
and for my actual projects interacting with an actual database.</p>
<p>We will be using <code>pytest</code> as our testing framework.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">ASGI</summary>
  
  ASGI (Asynchronous Server Gateway Interface) is a spiritual successor to WSGI, intended to provide a standard interface between async-capable Python web servers, frameworks, and applications. - <a href="https://asgi.readthedocs.io/en/latest/">https://asgi.readthedocs.io/en/latest/</a>
</details>

<h2 id="mainpy">main.py</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socketio</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">uvicorn</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">startup</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Starting Application&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">sio</span> <span class="o">=</span> <span class="n">socketio</span><span class="o">.</span><span class="n">AsyncServer</span><span class="p">(</span><span class="n">async_mode</span><span class="o">=</span><span class="s2">&#34;asgi&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">socketio</span><span class="o">.</span><span class="n">ASGIApp</span><span class="p">(</span><span class="n">sio</span><span class="p">,</span> <span class="n">on_startup</span><span class="o">=</span><span class="n">startup</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FooEvent</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@sio.on</span><span class="p">(</span><span class="s2">&#34;FOO&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">foo_event</span><span class="p">(</span><span class="n">sid</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">FooEvent</span><span class="p">(</span><span class="o">**</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">emit</span><span class="p">(</span><span class="s2">&#34;BAR&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;foo&#34;</span><span class="p">:</span> <span class="n">data</span><span class="o">.</span><span class="n">name</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">uvicorn</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="s2">&#34;0.0.0.0&#34;</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
</span></span></code></pre></div><p>Let&rsquo;s take a look at our socketio app. Which is a very simple web app, that listens to one event <code>FOO</code> and
responds with a <code>BAR</code> event. It is just this single file.</p>
<h2 id="conftestpy">conftest.py</h2>
<p>The <code>conftest.py</code> file is automatically run by pytest and allows our test modules to access fixtures defined
in this file. One of the best features of Pytest is fixtures. Fixture are functions that have re-usable bits of code we
can run in our tests, such as static data used by tests.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">asyncio</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">AsyncIterator</span><span class="p">,</span> <span class="n">Awaitable</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socketio</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">uvicorn</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">app</span> <span class="kn">import</span> <span class="n">main</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">socketio</span> <span class="kn">import</span> <span class="n">ASGIApp</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">socketio.asyncio_client</span> <span class="kn">import</span> <span class="n">AsyncClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">PORT</span> <span class="o">=</span> <span class="mi">8000</span>
</span></span><span class="line"><span class="cl"><span class="n">LISTENING_IF</span> <span class="o">=</span> <span class="s2">&#34;127.0.0.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE_URL</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;http://</span><span class="si">{</span><span class="n">LISTENING_IF</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">PORT</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UvicornTestServer</span><span class="p">(</span><span class="n">uvicorn</span><span class="o">.</span><span class="n">Server</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">:</span> <span class="n">ASGIApp</span> <span class="o">=</span> <span class="n">main</span><span class="o">.</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">LISTENING_IF</span><span class="p">,</span> <span class="n">port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">PORT</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Event</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Awaitable</span><span class="p">[</span><span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="n">uvicorn</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">startup</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Override uvicorn startup&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">startup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">setup_event_loop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">set</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">start_up</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Start up server asynchronously&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serve</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">tear_down</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Shut down server asynchronously&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">should_exit</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">event_loop</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">loop</span>
</span></span><span class="line"><span class="cl">    <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">startup_and_shutdown_server</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">=</span> <span class="n">UvicornTestServer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">start_up</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">tear_down</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">client</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">AsyncClient</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sio</span> <span class="o">=</span> <span class="n">socketio</span><span class="o">.</span><span class="n">AsyncClient</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">BASE_URL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">sio</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">disconnect</span><span class="p">()</span>
</span></span></code></pre></div><h3 id="quick-aside-fastapi-testing">Quick Aside FastAPI Testing</h3>
<details
  class="notice danger"
  open="true"
>
    <summary class="notice-title">Uvicorn</summary>
  
  tl:dr: We need to start and stop the Uvicorn server within our tests.
</details>

<p>Now when testing say a FastAPI application, it has a builtin test client we can use. This means we don&rsquo;t actually have
to spin up a Uvicorn server to test our application. We can simply pretend to send requests to the FastAPI web service
and it will handle the routing behind the scenes.</p>
<p>We can do something like this, where <code>httpx</code> is a async HTTP client (think like the <code>requests</code> library).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">asgi_lifespan</span> <span class="kn">import</span> <span class="n">LifespanManager</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">httpx</span> <span class="kn">import</span> <span class="n">AsyncClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">app.main</span> <span class="kn">import</span> <span class="n">app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">client</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">AsyncClient</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">with</span> <span class="n">LifespanManager</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">async</span> <span class="k">with</span> <span class="n">AsyncClient</span><span class="p">(</span><span class="n">app</span><span class="o">=</span><span class="n">app</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&#34;http://localhost&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">client</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">yield</span> <span class="n">client</span>
</span></span></code></pre></div><p>Then we can use it like so in our tests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">fastapi</span> <span class="kn">import</span> <span class="n">status</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">httpx</span> <span class="kn">import</span> <span class="n">AsyncClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.mark.asyncio</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">test_add_game</span><span class="p">(</span><span class="n">client</span><span class="p">:</span> <span class="n">AsyncClient</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="k">await</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&#34;/game&#34;</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">request_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span>
</span></span></code></pre></div><p>However socketio at the moment does not provide us with a test client we can use. So we will start and stop a Uvicorn server and send actual
Socketio requests from a Socketio client. There is a Socketio client library we can use to do this, available in Python.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UvicornTestServer</span><span class="p">(</span><span class="n">uvicorn</span><span class="o">.</span><span class="n">Server</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">:</span> <span class="n">ASGIApp</span> <span class="o">=</span> <span class="n">main</span><span class="o">.</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">LISTENING_IF</span><span class="p">,</span> <span class="n">port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">PORT</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Event</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Awaitable</span><span class="p">[</span><span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="n">uvicorn</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">startup</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Override uvicorn startup&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">startup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">setup_event_loop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">set</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">start_up</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Start up server asynchronously&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serve</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">def</span> <span class="nf">tear_down</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Shut down server asynchronously&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">should_exit</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span>
</span></span></code></pre></div><p>This is a test class which we can use to start and stop the Uvicorn server. Note that the class inherits
from <code>uvicorn.server</code>, we need to overwrite the <code>startup()</code> method as we want to change the startup a bit.</p>
<p>Before explaining the code above let&rsquo;s take a look at how we may use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">event_loop</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">loop</span>
</span></span><span class="line"><span class="cl">    <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">startup_and_shutdown_server</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">=</span> <span class="n">UvicornTestServer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">start_up</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">tear_down</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">client</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">AsyncClient</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sio</span> <span class="o">=</span> <span class="n">socketio</span><span class="o">.</span><span class="n">AsyncClient</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">BASE_URL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">sio</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">disconnect</span><span class="p">()</span>
</span></span></code></pre></div><p>What we have done is created two pytest fixtures, the first simply starts an event loop so we can test async code.</p>
<h3 id="tangent-on-asyncio">Tangent on asyncio</h3>
<p>To test async code with pytest we need to install the <code>pyest-asyncio</code> library.
By default this will give us an <code>event_loop</code> fixture that runs on scope of <code>function</code>. So it will start and stop after
each test function. However if you want to use fixtures that aren&rsquo;t of scope <code>function</code> i.e. <code>session</code> or <code>module</code>.
Then we need to redefine the <code>event_loop</code> function as we have done in the example above.</p>
<p>Okay back to our code above. The main bit we are interested in is the <code>startup_and_shutdown_server</code> function, here we
start the server before all of our tests and due to how <code>yield</code>, you can read more about how
<a href="/blog/python-yield-explained/">yield works here</a>, we will stop our server after all of our tests have run.</p>
<p>This happens automatically without calling the function because of the decorator we have provided
<code>@pytest.fixture(autouse=True, scope=&quot;session&quot;)</code>.
Again we are using scope <code>session</code> so that this function isn&rsquo;t called either for
every function (which would slow down our tests). We could&rsquo;ve set it to <code>module</code> but again
if we have multiple test files we don&rsquo;t want to run this function for every file (module).</p>
<h3 id="deeper-diver-into-uvicorntestserver">Deeper diver into UvicornTestServer</h3>
<p>Let&rsquo;s take a look at the first two methods</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">  <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">:</span> <span class="n">ASGIApp</span> <span class="o">=</span> <span class="n">main</span><span class="o">.</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">LISTENING_IF</span><span class="p">,</span> <span class="n">port</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">PORT</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Event</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Awaitable</span><span class="p">[</span><span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">      <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="n">uvicorn</span><span class="o">.</span><span class="n">Config</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">async</span> <span class="k">def</span> <span class="nf">startup</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;&#34;&#34;Override uvicorn startup&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">startup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">setup_event_loop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">set</span><span class="p">()</span>
</span></span></code></pre></div><p>The <code>__init__</code> magic dunder method creates an asyncio event <code>asyncio.Event()</code>. These events are often used to:</p>
<blockquote>
<p>An asyncio event can be used to notify multiple asyncio tasks that some event has happened. - <a href="https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event">https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event</a></p>
</blockquote>
<p>Then we create a variable <code>self._serve_task: Optional[Awaitable[Any]] = None</code>, we will see how this used later.
Finally we call the parent calls <code>__init__</code> method (<code>super().__init__()</code>). This calls the <code>__init__</code> function
of the <code>uvicorn.Server</code> class. We do this to set the <code>uvicorn.Config</code>, which includes our app and which host and port
to start the server.</p>
<p>Onto the second method <code>startup</code> this also overwrites a method in the parent class. In fact the first we do is call
the parent class&rsquo;s <code>startup</code> method (<code>await super().startup()</code>). Then we start the event loop ourselves
<code>self.config.setup_event_loop()</code>, where our web app will run.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Event Loop</summary>
  
  This is a different event loop in which our tests run in.
</details>

<p>Finally we do <code>self._startup_done.set()</code>, we are setting this event as true i.e. is complete. So any coroutines waiting
until this set can be carry on their execution.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Asyncio</summary>
  
  An Event object manages an internal flag that can be set to true with the set() method and reset to false with the clear() method. The wait() method blocks until the flag is set to true. The flag is set to false initially. - <a href="https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event">https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event</a>
</details>

<h4 id="yet-another-tangent-on-run-method">Yet another tangent on run() method</h4>
<p>Now the parent class does have a <code>run</code> method we could use, which would start the event loop for us. This however won&rsquo;t work,
lets pretend we change <code>startup_and_shutdown_server</code> function too look like this (<code>server.run()</code>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">startup_and_shutdown_server</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">=</span> <span class="n">UvicornTestServer</span><span class="p">()</span>
</span></span><span class="line hl"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">server</span><span class="o">.</span><span class="n">tear_down</span><span class="p">()</span>
</span></span></code></pre></div><p>We would get the following error <code>RuntimeError: asyncio.run() cannot be called from a running event loop</code>. This because if
we take a look at the <code>run</code> method in the parent class it contains something like this line
<code>return asyncio.run(self.serve(...))</code>.</p>
<p>This is why we need to write our own code to handle starting the Uvicorn server.</p>
<h4 id="start_up-and-tear_down"><code>start_up</code> and <code>tear_down</code></h4>
<p>Okay let&rsquo;s move and take a look at the <code>start_up</code> and <code>tear_down</code> methods</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">start_up</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serve</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_startup_done</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">tear_down</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">should_exit</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_serve_task</span>
</span></span></code></pre></div><p>Remember these are the two methods we will call in our &ldquo;startup and shutdown&rdquo; fixture. The <code>start_up</code> method, creates a task and assigns it
to our empty variable from the <code>__init__</code> method <code>self._serve_task = asyncio.create_task(self.serve())</code>. It calls the <code>serve</code> method to start
the Uvicorn server.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">What does create_task do?</summary>
  
  It submits the coroutine to run &ldquo;in the background&rdquo;, i.e. concurrently with the current task and all other tasks, switching between them at await points. It returns an awaitable handle called a &ldquo;task&rdquo; which you can also use to cancel the execution of the coroutine. - <a href="https://stackoverflow.com/questions/62528272/what-does-asyncio-create-task-do">https://stackoverflow.com/questions/62528272/what-does-asyncio-create-task-do</a>
</details>

<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">What is a task?</summary>
  
  It&rsquo;s an asyncio construct that tracks execution of a coroutine in a concrete event loop. When you call create_task, you submit a coroutine for execution and receive back a handle. You can await this handle when you actually need the result, or you can never await it, if you don&rsquo;t care about the result. This handle is the task, and it inherits from Future, which makes it awaitable and also provides the lower-level callback-based interface, such as add_done_callback. - <a href="https://stackoverflow.com/questions/62528272/what-does-asyncio-create-task-do">https://stackoverflow.com/questions/62528272/what-does-asyncio-create-task-do</a>
</details>

<p>Then we <code>await self._startup_done.wait()</code>, this is the event we created earlier. It will wait until the <code>set()</code> function
has been called in the in the <code>startup</code> method above.</p>
<p>Now onto the <code>tear_down</code> method where we set the <code>should_exit</code> to true. There is a <code>main_loop</code> method called by our
<code>serve</code> method in the parent class. This <code>main_loop</code> calls an <code>on_tick</code> function which returns if <code>self.should_exit</code> is true.
So the call chain looks like: <code>serve</code> -&gt; <code>main_loop</code> -&gt; <code>on_tick</code>. When on_tick returns <code>should_exist</code> as true, it exits it main loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">main_loop</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">should_exit</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_tick</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="ow">not</span> <span class="n">should_exit</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">%</span> <span class="mi">864000</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl">        <span class="n">should_exit</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_tick</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="client-fixture">Client Fixture</h3>
<p>Finally lets take a look at our final fixture, here we create a client that can be used to make requests with socketio.
We use a similar technique with <code>yields</code> so we return a socketio client. We will see how this used in one of our tests.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">client</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">AsyncClient</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sio</span> <span class="o">=</span> <span class="n">socketio</span><span class="o">.</span><span class="n">AsyncClient</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">BASE_URL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">sio</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">sio</span><span class="o">.</span><span class="n">disconnect</span><span class="p">()</span>
</span></span></code></pre></div><h2 id="test_roompy">test_room.py</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">asyncio</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">socketio.asyncio_client</span> <span class="kn">import</span> <span class="n">AsyncClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.mark.asyncio</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">test_success</span><span class="p">(</span><span class="n">client</span><span class="p">:</span> <span class="n">AsyncClient</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">future</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span><span class="o">.</span><span class="n">create_future</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@client.on</span><span class="p">(</span><span class="s2">&#34;BAR&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">future</span><span class="o">.</span><span class="n">set_result</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">client</span><span class="o">.</span><span class="n">emit</span><span class="p">(</span><span class="s2">&#34;FOO&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;haseeb&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">future</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">5.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">future</span><span class="o">.</span><span class="n">result</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">result</span> <span class="o">==</span> <span class="p">{</span><span class="s2">&#34;foo&#34;</span><span class="p">:</span> <span class="s2">&#34;haseeb&#34;</span><span class="p">}</span>
</span></span></code></pre></div><p>Since we need to wait for the <code>FOO</code> event to return a <code>BAR</code> event we use a future
to await until we get a response then set the return data in the future</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@client.on</span><span class="p">(</span><span class="s2">&#34;BAR&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">future</span><span class="o">.</span><span class="n">set_result</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span></code></pre></div><p>We <code>await asyncio.wait_for(future, timeout=5.0)</code> for the future to have data set on it.</p>
<p>That&rsquo;s it, the code itself is fairly simple once everything is setup in <code>conftest</code> to actually do the test.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2021-12-23-testing-a-socketio-web-app-written-in-python/source_code">Example source code</a></li>
<li><a href="https://github.com/miguelgrinberg/python-socketio/issues/332#issuecomment-712928157">Github Issue: UvicornTestServer</a></li>
<li><a href="https://stackoverflow.com/questions/62528272/what-does-asyncio-create-task-do">Async Create Task SO</a></li>
<li><a href="https://gitlab.com/banter-bus/banter-bus-core-api">Real application using this testing pattern</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing a Gatsby application with Cypress on Gitlab CI</title>
      <link>https://haseebmajid.dev/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/</link>
      <pubDate>Mon, 22 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/</guid>
      <description>&lt;p&gt;In this blog post, we will go over how we can automatically test a Gatsby site end-to-end (e2e), using Cypress on Gitlab CI.&lt;/p&gt;
&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;h2 id=&#34;gatsby&#34;&gt;Gatsby&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.gatsbyjs.com&#34;&gt;Gatsby&lt;/a&gt; is a static site generator (SSG) built upon React. It allows us to create &amp;ldquo;blazing&amp;rdquo; fast websites.
In this example, we will use a simple blog starter template available and add a Cypress test.&lt;/p&gt;
&lt;h2 id=&#34;cypress&#34;&gt;Cypress&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Fast, easy and reliable testing for anything that runs in a browser. - Cypress README&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this blog post, we will go over how we can automatically test a Gatsby site end-to-end (e2e), using Cypress on Gitlab CI.</p>
<h1 id="introduction">Introduction</h1>
<h2 id="gatsby">Gatsby</h2>
<p><a href="https://www.gatsbyjs.com">Gatsby</a> is a static site generator (SSG) built upon React. It allows us to create &ldquo;blazing&rdquo; fast websites.
In this example, we will use a simple blog starter template available and add a Cypress test.</p>
<h2 id="cypress">Cypress</h2>
<blockquote>
<p>Fast, easy and reliable testing for anything that runs in a browser. - Cypress README</p>
</blockquote>
<p><a href="http://cypress.io/">Cypress</a> allows us to test a web application, how a real user would use the application.
Cypress will be used to test our Gatsby application, though if it&rsquo;s a site you can test it using Cypress.</p>
<h2 id="gitlab-ci">Gitlab CI</h2>
<p><a href="https://docs.gitlab.com/ee/ci/">Gitlab CI</a> is a continuous integration pipeline that will allow us to run our
tests automatically, such as when we merge code into the master branch.</p>
<h1 id="getting-started">Getting Started</h1>
<h2 id="gatsby-1">Gatsby</h2>
<p>Create a new Gatsby site, using this default Gatsby starter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> gatsby-starter-blog
</span></span></code></pre></div><h3 id="optional-typescript">(Optional) Typescript</h3>
<p>Adding Typescript to a Gatsby web application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add typescript @types/react @types/react-dom @types/node -D
</span></span><span class="line"><span class="cl">yarn add gatsby-plugin-typescript
</span></span></code></pre></div><p>Add the following to your <code>gatsby-config.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-plugin-typescript`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">isTSX</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// defaults to false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">jsxPragma</span><span class="o">:</span> <span class="sb">`jsx`</span><span class="p">,</span> <span class="c1">// defaults to &#34;React&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">allExtensions</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// defaults to false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then create a new file <code>tsconfig.json</code> (in the project root, where the <code>gatsby-config.js</code> is).</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;compilerOptions&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;module&#34;</span><span class="p">:</span> <span class="s2">&#34;commonjs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;esnext&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;jsx&#34;</span><span class="p">:</span> <span class="s2">&#34;preserve&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;lib&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;dom&#34;</span><span class="p">,</span> <span class="s2">&#34;esnext&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;strict&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noEmit&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;isolatedModules&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;esModuleInterop&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noUnusedLocals&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allowJs&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;exclude&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;node_modules&#34;</span><span class="p">,</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span> <span class="s2">&#34;.cache&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="cypress-1">Cypress</h2>
<p>Now to finally add Cypress to our application so we can test it. First, install the dependencies.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cypress cypress-axe axe-core start-server-and-test
</span></span><span class="line"><span class="cl"><span class="c1"># Add types</span>
</span></span><span class="line"><span class="cl">yarn add -D @types/cypress-axe
</span></span></code></pre></div><p>Next, let&rsquo;s create a <code>cypress.json</code> folder in the project root.</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;baseUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;http://localhost:8000/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;integrationFolder&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress/e2e&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next, let&rsquo;s add some new &ldquo;scripts&rdquo; to the <code>package.json</code> file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line hl"><span class="cl">  <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cy:open&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress open&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cy:run&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress run&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby build&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;develop&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby develop&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;prettier --write \&#34;**/*.{js,jsx,ts,tsx,json,md}\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;start&#34;</span><span class="p">:</span> <span class="s2">&#34;npm run develop&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby serve&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;clean&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby clean&#34;</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl">    <span class="nt">&#34;test&#34;</span><span class="p">:</span> <span class="s2">&#34;echo \&#34;Write tests! -&gt; https://gatsby.dev/unit-testing\&#34; &amp;&amp; exit 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;test:e2e&#34;</span><span class="p">:</span> <span class="s2">&#34;start-server-and-test &#39;yarn develop&#39; http://localhost:8000 &#39;yarn cy:open&#39;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;test:e2e:ci&#34;</span><span class="p">:</span> <span class="s2">&#34;start-server-and-test &#39;yarn develop&#39; http://localhost:8000 &#39;yarn cy:run&#39;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>These scripts allow us to start Cypress, <code>cy:open</code> opens a GUI to visualise our tests whereas <code>cy:run</code> does it all
in the terminal (the browser runs in headless mode). Where we will run <code>test:e2e:ci</code> in our CI pipeline, here we use
the <code>start-server-and-test</code> command to start our Gatsby server using <code>yarn develop</code>. Then we run <code>cy:run</code> to
start our tests.</p>
<h3 id="structure">Structure</h3>
<p>Create a new folder called <code>cypress</code> which will look something like this.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── e2e
</span></span><span class="line"><span class="cl">│   └── accessibility.test.ts
</span></span><span class="line"><span class="cl">├── fixtures
</span></span><span class="line"><span class="cl">│   └── graphql.json
</span></span><span class="line"><span class="cl">├── plugins
</span></span><span class="line"><span class="cl">│   └── index.js
</span></span><span class="line"><span class="cl">├── support
</span></span><span class="line"><span class="cl">│   ├── commands.js
</span></span><span class="line"><span class="cl">│   ├── index.d.ts
</span></span><span class="line"><span class="cl">│   └── index.js
</span></span><span class="line"><span class="cl">└── tsconfig.json
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p cypress/support
</span></span></code></pre></div><p>Create a file at <code>cypress/support/commands.js</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">Cypress</span><span class="p">.</span><span class="nx">Commands</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="sb">`assertRoute`</span><span class="p">,</span> <span class="p">(</span><span class="nx">route</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">cy</span><span class="p">.</span><span class="nx">url</span><span class="p">().</span><span class="nx">should</span><span class="p">(</span><span class="sb">`equal`</span><span class="p">,</span> <span class="sb">`</span><span class="si">${</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="si">}${</span><span class="nx">route</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Add some custom types for Cypress <code>index.d.ts</code> if you are using Typescript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kr">declare</span> <span class="kr">namespace</span> <span class="nx">Cypress</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">interface</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">Subject</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Assert the current URL
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @param route
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @example cy.assertRoute(&#39;/page-2&#39;)
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="nx">assertRoute</span><span class="p">(</span><span class="nx">route</span>: <span class="kt">string</span><span class="p">)</span><span class="o">:</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">any</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Waits for Gatsby to finish the route change, in order to ensure event handlers are properly setup
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="nx">waitForRouteChange</span><span class="p">()</span><span class="o">:</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">any</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next create the <code>index.js</code> file, which should look something like this.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;cypress-axe&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./commands&#34;</span>
</span></span></code></pre></div><p>Next, let&rsquo;s create a plugin folder <code>mkdir -p cypress/plugins</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// / &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">// ***********************************************************
</span></span></span><span class="line"><span class="cl"><span class="c1">// This example plugins/index.js can be used to load plugins
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// You can change the location of this file or turn off loading
</span></span></span><span class="line"><span class="cl"><span class="c1">// the plugins file with the &#39;pluginsFile&#39; configuration option.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// You can read more here:
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://on.cypress.io/plugins-guide
</span></span></span><span class="line"><span class="cl"><span class="c1">// ***********************************************************
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// This function is called when a project is opened or re-opened (e.g. due to
</span></span></span><span class="line"><span class="cl"><span class="c1">// the project&#39;s config changing)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @type {Cypress.PluginConfig}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">(</span><span class="nx">on</span><span class="p">,</span> <span class="nx">config</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// `on` is used to hook into various events Cypress emits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// `config` is the resolved Cypress config
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">return</span> <span class="nx">config</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now finally let&rsquo;s create our tests folder <code>mkdir -p cypress/e2e</code>.</p>
<h3 id="cypress-axe">cypress-axe</h3>
<p>In this blog post we won&rsquo;t go over any complicated Cypress test we will simply use <code>cypress-axe</code> to test
the accessibility of our (a11y) of our website.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;../support/index&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;@types/cypress-axe&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">describe</span><span class="p">(</span><span class="s2">&#34;Component accessibility test&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">it</span><span class="p">(</span><span class="s2">&#34;Main Page&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">injectAxe</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">checkA11y</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">include</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;#___gatsby&#34;</span><span class="p">]],</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><p>Note the <code>///</code> comments at the top used to add types for cypress. The test above will go to our
home page and test if it has any a11y violations and if so will fail the test.</p>
<p>We can now run our tests locally by running this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn run test:e2e
</span></span></code></pre></div><h2 id="gitlab-ci-1">Gitlab CI</h2>
<p>Now how can we automate this, so the tests will run say every time we make changes on the master branch to make
sure we haven&rsquo;t broken any a11y. Create a new <code>.gitlab-ci.yml</code> or add the following job to an existing CI 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">image</span><span class="p">:</span><span class="w"> </span><span class="l">node:12.14.1</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="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">CYPRESS_CACHE_FOLDER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;$CI_PROJECT_DIR/cache/Cypress&#34;</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="w"></span><span class="nt">cache</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${CI_COMMIT_REF_SLUG}</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">cache/Cypress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">node_modules</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="w"></span><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="w"></span><span class="nt">before_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">yarn install</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="w"></span><span class="nt">tests</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:node12.14.1-chrome83-ff77</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">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">yarn test:e2e:ci</span><span class="w">
</span></span></span></code></pre></div><p>I won&rsquo;t go into the details of what makes up a Gitlab CI file. At the top of the file, we will cache the <code>node_modules</code>
file so we can share it between the job and the Cypress cache.
The job itself is very simple it uses a <code>cypress/browsers:node12.14.1-chrome83-ff77</code> Docker
image which provides a headless chrome browser that Cypress can leverage to run the tests.
As we won&rsquo;t have access to a GUI in the Gitlab CI runner. The <code>tests</code> job is very simple it runs <code>yarn test:e2e:ci</code> to
run our Cypress tests.</p>
<p>That&rsquo;s it, quite simple to add Cypress tests that run in our CI pipeline.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/source_code">Example source code</a></li>
<li><a href="http://cypress.io/">Cypress</a></li>
<li><a href="https://gitlab.com/hmajid2301/portfolio-site/-/jobs/1080367107">Example Job</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing a Connexion/Flask Application with Pytest</title>
      <link>https://haseebmajid.dev/posts/2020-06-09-testing-a-connexion-flask-application-with-pytest/</link>
      <pubDate>Tue, 09 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2020-06-09-testing-a-connexion-flask-application-with-pytest/</guid>
      <description>&lt;p&gt;In this article, I will show you how you can test a Python web service that was built using &lt;a href=&#34;https://github.com/zalando/connexion/&#34;&gt;Connexion&lt;/a&gt;
(a wrapper library around Flask). We will go over how you can mock functions and how you can test
your endpoints. There are two related articles I have written in the past listed below. In the first
one we go over how to create a web service using Connexions, the same web service we will in this article.
In the second article I introduce how you can use &lt;code&gt;pytest-mock&lt;/code&gt; and &lt;code&gt;pytest-flask&lt;/code&gt; to test a Flask web
service.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this article, I will show you how you can test a Python web service that was built using <a href="https://github.com/zalando/connexion/">Connexion</a>
(a wrapper library around Flask). We will go over how you can mock functions and how you can test
your endpoints. There are two related articles I have written in the past listed below. In the first
one we go over how to create a web service using Connexions, the same web service we will in this article.
In the second article I introduce how you can use <code>pytest-mock</code> and <code>pytest-flask</code> to test a Flask web
service.</p>
<ul>
<li><a href="/blog/rest-api-openapi-flask-connexion/">Implementing a Simple REST API using OpenAPI, Flask &amp; Connexions</a></li>
<li><a href="/blog/testing-with-pytest-mock-and-pytest-flask/">Testing with pytest-mock and pytest-flask</a></li>
</ul>
<p>The example app we will be writing tests for is a very simple CRUD API managing a pet store. It allows us
to add pets, remove pets, update pets and query pets we have in the store.</p>
<h2 id="structure">Structure</h2>
<p>You can find the source code here. Our project structure looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── openapi
</span></span><span class="line"><span class="cl">│   └── specification.yml
</span></span><span class="line"><span class="cl">├── requirements.txt
</span></span><span class="line"><span class="cl">├── test_api
</span></span><span class="line"><span class="cl">│   ├── core
</span></span><span class="line"><span class="cl">│   │   ├── __init__.py
</span></span><span class="line"><span class="cl">│   │   ├── pets.json
</span></span><span class="line"><span class="cl">│   │   └── pets.py
</span></span><span class="line"><span class="cl">│   ├── __init__.py
</span></span><span class="line"><span class="cl">│   ├── run.py
</span></span><span class="line"><span class="cl">│   └── web
</span></span><span class="line"><span class="cl">│       ├── controllers
</span></span><span class="line"><span class="cl">│       │   ├── __init__.py
</span></span><span class="line"><span class="cl">│       │   └── pets_controller.py
</span></span><span class="line"><span class="cl">│       ├── encoder.py
</span></span><span class="line"><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="cl">│       ├── models
</span></span><span class="line"><span class="cl">│       │   ├── base_model_.py
</span></span><span class="line"><span class="cl">│       │   ├── __init__.py
</span></span><span class="line"><span class="cl">│       │   ├── pet.py
</span></span><span class="line"><span class="cl">│       │   └── pets.py
</span></span><span class="line"><span class="cl">│       └── util.py
</span></span><span class="line"><span class="cl">└── tests
</span></span><span class="line"><span class="cl">    ├── conftest.py
</span></span><span class="line"><span class="cl">    ├── __init__.py
</span></span><span class="line"><span class="cl">    └── test_pets_controller.py
</span></span></code></pre></div><h2 id="api">API</h2>
<p>Here is our controller module called <code>web/controller/pets_controller.py</code>. This is where connexion routes are requests to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">connexion</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">six</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">..models.pet</span> <span class="kn">import</span> <span class="n">Pet</span>  <span class="c1"># noqa: E501</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">..models.pets</span> <span class="kn">import</span> <span class="n">Pets</span>  <span class="c1"># noqa: E501</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">util</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">test_api.core</span> <span class="kn">import</span> <span class="n">pets</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_pet</span><span class="p">(</span><span class="n">pet_id</span><span class="p">):</span>  <span class="c1"># noqa: E501</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get a pet in the store
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">     # noqa: E501
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param pet_id: The id of the pet to retrieve
</span></span></span><span class="line"><span class="cl"><span class="s2">    :type pet_id: str
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    :rtype: Pet
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">pet</span> <span class="o">=</span> <span class="n">pets</span><span class="o">.</span><span class="n">get_pet</span><span class="p">(</span><span class="n">pet_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">response</span> <span class="o">=</span> <span class="n">Pet</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">breed</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">breed</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">price</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">price</span><span class="p">),</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">response</span> <span class="o">=</span> <span class="p">{},</span> <span class="mi">404</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span>
</span></span></code></pre></div><p>Connexion uses the open API specification <code>openapi/specification.yml</code>, to work out which function to route requests
for the path <code>/pet/{pet_id}</code>. It uses the <code>operationId</code> alongside the <code>x-swagger-router-controller</code> to determine
the function to call in the <code>pets_controller.py</code> module.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">/pet/{pet_id}:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">get</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">tags</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;pet&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">summary</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Get a pet in the store&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">operationId</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;get_pet&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">parameters</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="s2">&#34;pet_id&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">in</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;path&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;The id of the pet to retrieve&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">required</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;string&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">responses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">200</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Successfully retrived pet&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">schema</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">$ref</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;#/definitions/Pet&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">404</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Pet doesn&#39;t exist&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">x-swagger-router-controller</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;test_api.web.controllers.pets_controller&#34;</span><span class="w">
</span></span></span></code></pre></div><h2 id="tests">Tests</h2>
<p>Now onto our tests!</p>
<h3 id="libraries">Libraries</h3>
<p><em>pytest-flask</em> allows us to specify an app fixture and then send API requests with this app. Usage is similar to the <code>requests</code> library when sending HTTP requests to our app.</p>
<p><em>pytest-mock</em> is a simple wrapper around the unit test mock library, so anything you can do using <code>unittest.mock</code> you can do with <code>pytest-mock</code>. The main difference in usage is you can access it using a fixture <code>mocker</code>, also the mock ends at the end of the test. Whereas with the normal mock library if you say mock the <code>open()</code> function, it will be mocked for the remaining duration of that test module, i.e. it will affect other tests.</p>
<h3 id="conftestpy">conftest.py</h3>
<p>The <code>conftest.py</code> file is automatically run by pytest and allows our test modules to access fixtures defined
in this file. One of the best features of Pytest is fixtures. Fixture are functions that have re-usable bits of code we
can run in our unit tests, such as static data used by tests.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">test_api.run</span> <span class="kn">import</span> <span class="n">create_app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">app</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">abs_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">openapi_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">abs_file_path</span><span class="p">,</span> <span class="s2">&#34;../&#34;</span><span class="p">,</span> <span class="s2">&#34;openapi&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&#34;SPEC_PATH&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">openapi_path</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">app</span> <span class="o">=</span> <span class="n">create_app</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">,</span> <span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">clean_up</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="n">default_pets</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;1&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ginger&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;bengal&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;2&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;sam&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;husky&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;3&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;guido&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;python&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">518</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">abs_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">json_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">abs_file_path</span><span class="p">,</span> <span class="s2">&#34;../&#34;</span><span class="p">,</span> <span class="s2">&#34;test_api&#34;</span><span class="p">,</span> <span class="s2">&#34;core&#34;</span><span class="p">,</span> <span class="s2">&#34;pets.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">json_path</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">pet_store</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">default_pets</span><span class="p">,</span> <span class="n">pet_store</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="app">app()</h4>
<p>In this file, we have two functions: the <code>app</code> allows users to pass the <code>client</code> argument to other tests
and then we can test our web application. You can get more information
<a href="https://flask.palletsprojects.com/en/1.1.x/testing/#the-testing-skeleton">here</a> about how Flask apps can be tested.
Essentially we don&rsquo;t need to start/stop a server before/after our tests.</p>
<p>By giving it the <code>scope=session</code> the fixture will be created once before all of our tests run. Our <code>run.py</code> file looks
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">connexion</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">.web</span> <span class="kn">import</span> <span class="n">encoder</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_app</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="s2">&#34;SPEC_PATH&#34;</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">openapi_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&#34;SPEC_PATH&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">abs_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">openapi_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">abs_file_path</span><span class="p">,</span> <span class="s2">&#34;../&#34;</span><span class="p">,</span> <span class="s2">&#34;../&#34;</span><span class="p">,</span> <span class="s2">&#34;openapi&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span> <span class="o">=</span> <span class="n">connexion</span><span class="o">.</span><span class="n">FlaskApp</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="vm">__name__</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">specification_dir</span><span class="o">=</span><span class="n">openapi_path</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">options</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;swagger_ui&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">&#34;serve_spec&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span><span class="o">.</span><span class="n">add_api</span><span class="p">(</span><span class="s2">&#34;specification.yml&#34;</span><span class="p">,</span> <span class="n">strict_validation</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">flask_app</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">app</span>
</span></span><span class="line"><span class="cl">    <span class="n">flask_app</span><span class="o">.</span><span class="n">json_encoder</span> <span class="o">=</span> <span class="n">encoder</span><span class="o">.</span><span class="n">JSONEncoder</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">flask_app</span>
</span></span></code></pre></div><p>The <code>create_app</code> function creates our web application and returns a Flask object. Remember the Connexion library is
just a wrapper around Flask. Connexion just reduces the boilerplate code we wrote. Again you can have a read of the
article above to get more details about how it works.</p>
<h4 id="clean_up">clean_up()</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&#34;session&#34;</span><span class="p">,</span> <span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">clean_up</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="n">default_pets</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;1&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ginger&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;bengal&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;2&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;sam&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;husky&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;3&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;guido&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;python&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">518</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">abs_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">json_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">abs_file_path</span><span class="p">,</span> <span class="s2">&#34;../&#34;</span><span class="p">,</span> <span class="s2">&#34;test_api&#34;</span><span class="p">,</span> <span class="s2">&#34;core&#34;</span><span class="p">,</span> <span class="s2">&#34;pets.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">json_path</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">pet_store</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">default_pets</span><span class="p">,</span> <span class="n">pet_store</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</span></span></code></pre></div><p>The second fixture we define is called <code>clean_up</code>, because of the <code>yield</code> line, this function will run after all of
our tests have completed. The <code>yield</code> command is related to generators, you can read
<a href="https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do">more here</a>. In our case, it&rsquo;s used in Pytest
fixtures so that we can run some cleanup jobs after our test is completed. In this example, I am simply replacing the
contents of the JSON file which acts as a data store (like a database), to its default values before the test was run.</p>
<blockquote>
<p>Since pytest-3.0, fixtures using the normal fixture decorator can use a yield statement to provide fixture values and execute teardown code - Pytest Docs</p>
</blockquote>
<h3 id="test_pets_controllerpy">test_pets_controller.py</h3>
<p>Now we have gone over the setup required for our tests, let&rsquo;s take a look at how we can test our
code. So our first test looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_get_all_pets</span><span class="p">(</span><span class="n">client</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;/api/v1/pet&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">expected_json</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ginger&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;bengal&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;2&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;sam&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;husky&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;3&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;guido&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;python&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">518</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span> <span class="o">==</span> <span class="n">expected_json</span>
</span></span></code></pre></div><p>It&rsquo;s a very simple test, here we use the <code>app</code> fixture we defined above. This <code>client</code> fixture can be used
because we are using the <code>pytest-flask</code> library. As you can see it looks very similar to <code>requests</code>, where
we give it a path <code>/API/v1/pet</code> and then tell it what kind of request to make <code>client.get</code>.
Whilst the syntax between the <code>requests</code> library and the <code>client</code> fixture is almost identical. One big
difference that always seems to trip me up is, in <code>requests</code> to get the JSON data from the <code>response</code> object would be
<code>response.json()</code> i.e it is a function. However in <code>client</code> (<code>pytest-flask</code>) fixture do get the JSON data we do
<code>response.json</code> which is just an attribute of the object not a function.</p>
<p>The test itself is very simple, it&rsquo;s making a request to get all pets in the pet store. We then compare that with=
what we expect to be in the pet store <code>assert response.json == expected_json</code>.</p>
<p>The next test we have looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@pytest.mark.parametrize</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;pet_data, expected_status, expected_data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">({</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Yolo&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;shorthair&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">},</span> <span class="mi">201</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">        <span class="p">({},</span> <span class="mi">400</span><span class="p">,</span> <span class="p">{}),</span>
</span></span><span class="line"><span class="cl">        <span class="p">({</span><span class="s2">&#34;a&#34;</span><span class="p">:</span> <span class="s2">&#34;b&#34;</span><span class="p">},</span> <span class="mi">400</span><span class="p">,</span> <span class="p">{}),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_add_a_pet</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">pet_data</span><span class="p">,</span> <span class="n">expected_status</span><span class="p">,</span> <span class="n">expected_data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;/api/v1/pet&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">pet_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="n">expected_status</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span> <span class="o">==</span> <span class="n">expected_data</span>
</span></span></code></pre></div><p>This test is attempting to add a new pet to the store. It&rsquo;s similar to the other test we still use
the <code>client</code> fixture to make the request. This time we also give it some <code>json</code> data hence we provide the <code>json</code>
argument <code>json=pet_data</code> this automatically sets the headers correctly so the server knows it&rsquo;s receiving
JSON data.</p>
<p>We also use a decorate called <code>@pytest.mark.parametrize</code>. This allows us to run our tests against a list
of data. So we don&rsquo;t have to write the same test x number of times. We just pass the test different
arguments. Pytest will run this test x number of times once for each item in the list.
So, for example, the first time the test runs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">pet_data</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Yolo&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;shorthair&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">expected_status</span> <span class="o">=</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl"><span class="n">expected_data</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
</span></span></code></pre></div><p>The second like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">pet_data</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">expected_status</span> <span class="o">=</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl"><span class="n">expected_data</span> <span class="o">=</span> <span class="p">{}</span>
</span></span></code></pre></div><p>And so on and so on. This helps keep our test file smaller and keeps the DRY (do not repeat yourself).
A very nice feature of Pytest and one I use heavily.</p>
<p>The final test we have in this file looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_add_pet_fail_json</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">mocker</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">pet_data</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Yolo&#34;</span><span class="p">,</span> <span class="s2">&#34;breed&#34;</span><span class="p">:</span> <span class="s2">&#34;shorthair&#34;</span><span class="p">,</span> <span class="s2">&#34;price&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;/api/v1/pet&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mock</span> <span class="o">=</span> <span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;connexion.request&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mock</span><span class="o">.</span><span class="n">is_json</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">pet_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">400</span>
</span></span></code></pre></div><p>At last, we see <code>pytest-mock</code> being used via the <code>mocker</code> fixture we automatically get access to.
The <code>mocker</code> is just a simple wrapper around the <code>unittest.mock</code> module. The main difference being
the mock on exists for the duration of that test. Mocking is often used when unit testing and we cannot
rely on external dependencies such as database connections or another web service.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add_pet</span><span class="p">(</span><span class="n">body</span><span class="p">):</span>  <span class="c1"># noqa: E501</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">connexion</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">is_json</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">body</span> <span class="o">=</span> <span class="n">Pet</span><span class="o">.</span><span class="n">from_dict</span><span class="p">(</span><span class="n">connexion</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">get_json</span><span class="p">())</span>  <span class="c1"># noqa: E501</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span></code></pre></div><p>In this example, we want to mock the part of connexion that checks if the data being sent is valid JSON.
We want the <code>connexion.request.is_json</code> to return <code>False</code>, we can do this like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">mock</span> <span class="o">=</span> <span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;connexion.request&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">mock</span><span class="o">.</span><span class="n">is_json</span> <span class="o">=</span> <span class="kc">False</span>
</span></span></code></pre></div><p>Since <code>is_json</code> is an attribute of the <code>connexion.request</code> module and not a function we need to set
it false on another line. If <code>is_json</code> was a function that we wanted to return <code>False</code> we could&rsquo;ve done
<code>mocker.patch(&quot;connexion.request.is_json&quot;)</code> instead.</p>
<p>You can run the tests locally by running the <code>pytest</code> command or if you want to run the code in this article, you can
by doing the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcl https://gitlab.com/hmajid2301/articles.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> 27.<span class="se">\ </span>Mocking<span class="se">\ </span>in<span class="se">\ </span>Flask<span class="se">\ </span>with<span class="se">\ </span>Pytest/source_code
</span></span><span class="line"><span class="cl">virtualenv .venv
</span></span><span class="line"><span class="cl"><span class="nb">source</span> .venv/bin/activate
</span></span><span class="line"><span class="cl">pip install -r requirements.txt
</span></span><span class="line"><span class="cl">pytest
</span></span></code></pre></div><p>That&rsquo;s it, the examples above cover most of the things you&rsquo;ll need to mock and test your connexion
web service.</p>
<blockquote>
<p>INFO: <code>pytest-flask</code> provides a whole bunch of other features that may be useful, you can find the full list <a href="https://pytest-flask.readthedocs.io/en/latest/features.html">here</a></p>
</blockquote>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-06-09-testing-a-connexion-flask-application-with-pytest/source_code">Example source code</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing a Flask App with pytest-mock and pytest-flask</title>
      <link>https://haseebmajid.dev/posts/2018-12-15-testing-a-flask-app-with-pytest-mock-and-pytest-flask/</link>
      <pubDate>Sat, 15 Dec 2018 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2018-12-15-testing-a-flask-app-with-pytest-mock-and-pytest-flask/</guid>
      <description>&lt;p&gt;Pytest is a popular Python library used for testing. It is my preferred testing library because it requires less boilerplate code than the alternatives such as (the builtin) unittest, the built in testing library.
In this article, I will show you how you can use &lt;code&gt;pytest-flask&lt;/code&gt; and &lt;code&gt;pytest-mock&lt;/code&gt; to test your Flask app. These two
libraries are plugins for Pytest which build upon some of the features that Pytest provides us.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Pytest is a popular Python library used for testing. It is my preferred testing library because it requires less boilerplate code than the alternatives such as (the builtin) unittest, the built in testing library.
In this article, I will show you how you can use <code>pytest-flask</code> and <code>pytest-mock</code> to test your Flask app. These two
libraries are plugins for Pytest which build upon some of the features that Pytest provides us.</p>
<p>In this example, we will be testing a very simple Flask app I created <a href="https://gitlab.com/hmajid2301/articles/-/tree/master/9.%20Testing%20with%20pytest-mock%20and%20pytest-flask/source_code">source code here</a>.
If you want to learn more about the Flask app you can read my previous article <a href="/posts/2018-11-24-building-a-simple-flask-app-with-sqlalchemy-and-docker/">here</a> :plug: :plug:.
All you really need to know for this article is that it involves using a very simple RESTful API for an imaginary cat store. Essentially all the API does is it interacts with a database,
to get current cats, add new cats, edit already existing cats and remove cats from the store.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Your own Flask app</li>
<li>Install the following dependencies, using <code>pip install -r requirements.txt</code> (or pip3 instead of pip)</li>
</ul>
<p>Where requirements.txt is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">attrs==19.3.0
</span></span><span class="line"><span class="cl">click==7.1.2
</span></span><span class="line"><span class="cl">Flask==1.1.2
</span></span><span class="line"><span class="cl">Flask-SQLAlchemy==2.4.3
</span></span><span class="line"><span class="cl">itsdangerous==1.1.0
</span></span><span class="line"><span class="cl">Jinja2==2.11.2
</span></span><span class="line"><span class="cl">MarkupSafe==1.1.1
</span></span><span class="line"><span class="cl">more-itertools==8.4.0
</span></span><span class="line"><span class="cl">packaging==20.4
</span></span><span class="line"><span class="cl">pluggy==0.13.1
</span></span><span class="line"><span class="cl">psycopg2==2.8.5
</span></span><span class="line"><span class="cl">py==1.8.2
</span></span><span class="line"><span class="cl">pyparsing==2.4.7
</span></span><span class="line"><span class="cl">pytest==5.4.3
</span></span><span class="line"><span class="cl">pytest-flask==1.0.0
</span></span><span class="line"><span class="cl">pytest-mock==3.1.1
</span></span><span class="line"><span class="cl">six==1.15.0
</span></span><span class="line"><span class="cl">SQLAlchemy==1.3.17
</span></span><span class="line"><span class="cl">wcwidth==0.2.4
</span></span><span class="line"><span class="cl">Werkzeug==1.0.1
</span></span></code></pre></div><h2 id="libraries">Libraries</h2>
<p><code>pytest-flask</code>: Allows us to specify an app fixture and then send API requests with this app. Usage is similar to <code>requests</code> library, when sending HTTP requests to our flask app.</p>
<p><code>pytest-mock</code>: Is a simple wrapper around the unittest mock library, so anything you can do using <code>unittest.mock</code> you can do with <code>pytest-mock</code>. The main difference in usage is you can access it using a fixture <code>mocker</code>, also the mock ends at the end of the test. Whereas with the normal mock library if you mock say the <code>open()</code> function, it will be mocked for the remaining duration of that test module, i.e. it will effect other tests.</p>
<h2 id="pytest-flask-example">pytest-flask example</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">example_app</span> <span class="kn">import</span> <span class="n">create_app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">app</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span> <span class="o">=</span> <span class="n">create_app</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_example</span><span class="p">(</span><span class="n">client</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
</span></span></code></pre></div><p>To use pytest-flask we need to create a fixture called <code>app()</code> which creates our Flask server. We can then use this fixture by passing <code>client</code>
as an argument to any test. Then we can send various http requests using <code>client</code>.</p>
<p>Above is a very simple example using pytest-flask, we send a GET request to our app, which should return all cats in the database.
We then check that the status code returned from the server was a 200 (OK). This is great except how can we mock out certain features within our code?</p>
<h2 id="pytest-mock">pytest-mock</h2>
<p>We can mock out certain parts of our code using the <code>pytest-mock</code> library, but we have to mock inside the <code>app()</code> fixture. Since the rest of our tests will just be making HTTP requests to our Flask server. In this example say we don&rsquo;t want to mock a connection to the database, we can use the following lines of code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;flask_sqlalchemy.SQLAlchemy.init_app&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;flask_sqlalchemy.SQLAlchemy.create_all&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;example.database.get_all&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="p">{})</span>
</span></span></code></pre></div><p>What this bit of code is doing is any time the any of the mocked functions are called, say <code>init_app</code> we mock it so it will always return true. We have to give it the &ldquo;full path&rdquo; to the function, it&rsquo;s the same as if you had to import the function itself.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">example.app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">app</span><span class="p">(</span><span class="n">mocker</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;flask_sqlalchemy.SQLAlchemy.init_app&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;flask_sqlalchemy.SQLAlchemy.create_all&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">&#34;example.database.get_all&#34;</span><span class="p">,</span> <span class="n">return_value</span><span class="o">=</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">example</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">app</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_example</span><span class="p">(</span><span class="n">client</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
</span></span></code></pre></div><p>In this example it&rsquo;s very boring as when we send an HTTP GET request to the app it will not interact with the database since we&rsquo;ve mocked this out but instead just return an empty dict ({}). In reality, this is
not a very good test, you would make it a bit more interesting.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">virtualenv .venv
</span></span><span class="line"><span class="cl"><span class="nb">source</span> .venv/bin/activate
</span></span><span class="line"><span class="cl">pip install -e .
</span></span><span class="line"><span class="cl">pip install -r requirements.txt
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="k">$(</span>xargs &lt; database.conf<span class="k">)</span>
</span></span><span class="line"><span class="cl">pytest
</span></span></code></pre></div><h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2018-12-15-testing-a-flask-app-with-pytest-mock-and-pytest-flask/source_code">Example source code</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Pytest with Background Thread Fixtures</title>
      <link>https://haseebmajid.dev/posts/2018-11-05-pytest-with-background-thread-fixtures/</link>
      <pubDate>Mon, 05 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2018-11-05-pytest-with-background-thread-fixtures/</guid>
      <description>&lt;p&gt;Recently I had to test some of my Python &amp;#x1f40d; &amp;#x1f40d; &amp;#x1f40d; code which required an external dependency and communicating by using TCP sockets &amp;#x1f50c; . You can think of this dependency as essentially a database because it stored information. However, when testing my Python code, I couldn&amp;rsquo;t rely on there always being a TCP server to send messages to.&lt;/p&gt;
&lt;p&gt;So I ended up creating a simplified mocked version in Python. This way I could run automated tests on my code without needing to either install/rely on this server already existing there. Essentially I ended up creating a TCP server in Python, which would receive some input and respond how I would expect the real TCP server too.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Recently I had to test some of my Python &#x1f40d; &#x1f40d; &#x1f40d; code which required an external dependency and communicating by using TCP sockets &#x1f50c; . You can think of this dependency as essentially a database because it stored information. However, when testing my Python code, I couldn&rsquo;t rely on there always being a TCP server to send messages to.</p>
<p>So I ended up creating a simplified mocked version in Python. This way I could run automated tests on my code without needing to either install/rely on this server already existing there. Essentially I ended up creating a TCP server in Python, which would receive some input and respond how I would expect the real TCP server too.</p>
<p>I write my tests using Pytest for several reasons, I prefer it to unittest because it required less boilerplate code. It also has lots of useful features such as fixtures. However, when creating tests for my API I realised that I had to first start the fake TCP server. But since it was always listening &#x1f442; for traffic &#x1f6a6; it would block the rest of my tests from running. So in this example, I will show you how you can create a Pytest fixture and run it on a background thread (TCP server) and still run your tests as normal.</p>
<h2 id="code">Code</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">TCPServer</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                                       <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                                  <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">9500</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="bp">self</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exception_type</span><span class="p">,</span> <span class="n">exception_value</span><span class="p">,</span> <span class="n">traceback</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">listen_for_traffic</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">connection</span><span class="p">,</span> <span class="n">address</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_sock</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="n">message</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">response</span> <span class="o">=</span> <span class="s2">&#34;Received&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="n">connection</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">                <span class="n">connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span></code></pre></div><p>This is a very basic TCP server, it received some input but always replies with &ldquo;Received&rdquo;. Also, I created the class to be used with the context manager so that the socket can be closed, hence the <em>__enter__()</em> and <em>__exit__()</em> dunder (magic) method. This TCP server will always listen on localhost on port 9500, this is the same port the client will also have to connect to, to send messages to this TCP server.</p>
<p>The main function of interest is <em>listen_for_traffic()</em>, which loops forever whilst listening for traffic. If it receives any data, it always replies with &ldquo;Received&rdquo;, of course with a real server we might want to do something more interesting but to keep this example simple that&rsquo;s all this server will do.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">socket</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">pytest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">.tcp_server</span> <span class="kn">import</span> <span class="n">TCPServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@pytest.fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">dummy_tcp_server</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">tcp_server</span> <span class="o">=</span> <span class="n">TCPServer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">example_server</span> <span class="k">as</span> <span class="n">tcp_server</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">example_server</span><span class="o">.</span><span class="n">listen_for_traffic</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">thread</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="n">example_server</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_example</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">HOST</span> <span class="o">=</span> <span class="s1">&#39;127.0.0.1&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PORT</span> <span class="o">=</span> <span class="mi">9500</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="k">as</span> <span class="n">s</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;Hello, world&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">data</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Received&#34;</span>
</span></span></code></pre></div><p>When you run the Pytest command it will automatically run tests on all modules and method that start with &ldquo;test_&rdquo;. I have created a fixture (using the fixture decorator), fixtures allow for code reuse within a Pytest module. Fixtures are typically used to connect to databases, fixtures are the run before any tests hence we can also use them to setup is code.</p>
<p>So taking a look at the <em>dummy_tcp_server()</em> function, first we use a context manager (with) to set up our TCP server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">with</span> <span class="n">tcp_server</span> <span class="k">as</span> <span class="n">example_server</span><span class="p">:</span>
</span></span></code></pre></div><p>This calls the <em>__enter__()</em> dunder method, which starts up our TCP server and so it&rsquo;s now listening for any traffic on port 9500.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">example_server</span><span class="o">.</span><span class="n">listen_for_traffic</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">thread</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p>This is the bit of code that actually runs our TCP server on another (background) thread. So the in the first line we tell the thread what function to call, <em>listen_for_traffic</em> (without brackets) has an infinite while loops just waiting to listen for traffic arriving on port 9500. We set the thread as a daemon so that when we close the main Python program the thread will automatically kill itself, this means we don&rsquo;t have to manage it yourself.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">yield</span> <span class="n">example_server</span>
</span></span></code></pre></div><p>The yield is very similar to a return statement. The main reason I choose to include is that if the tests for some reason raise an exception, the yield will still allow the context manager to exit. If we had any tear down code after the yield even with an exception, this code will still run after all the tests have finished. Also yields (and return statements) allow individual tests to access functions from the TCPServer class. For example, if you had a function (in the TCPServer class) that stored every message sent to the server. If you pass the fixture as a parameter to any tests then you could access this function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">test_example</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">HOST</span> <span class="o">=</span> <span class="s1">&#39;127.0.0.1&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PORT</span> <span class="o">=</span> <span class="mi">9500</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="k">as</span> <span class="n">s</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;Hello World&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">data</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Received&#34;</span>
</span></span></code></pre></div><p>The part of our code is the actual test. It sends &ldquo;Hello World&rdquo; to the TCP server, remember if you sent it as string (without <em>b&rsquo;&rsquo;</em>) then you&rsquo;ll have to encode that string first (<em>&ldquo;Hello World&rdquo;.encode()</em>). Then we capture the response from the server and decode it (because it&rsquo;s in binary, this converts it into a string) and assert or check that the data is the string &ldquo;Received&rdquo; as we expect..</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2018-11-05-pytest-with-background-thread-fixtures/source_code">Example source code</a></li>
<li><a href="https://docs.pytest.org/en/latest/">Pytest</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
