<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Namah Shrestha</title>
    <description>The latest articles on DEV Community by Namah Shrestha (@zim95).</description>
    <link>https://dev.to/zim95</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg</url>
      <title>DEV Community: Namah Shrestha</title>
      <link>https://dev.to/zim95</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zim95"/>
    <language>en</language>
    <item>
      <title>Chapter 6: Test on Commit With Github Actions</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 12:37:26 +0000</pubDate>
      <link>https://dev.to/zim95/chapter-6-test-on-commit-with-github-actions-39po</link>
      <guid>https://dev.to/zim95/chapter-6-test-on-commit-with-github-actions-39po</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;The chapter builds upon the project structure from the previous chapter (Chapter 5). I suggest reading the chapter to understand the project structure. &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-6-test-on-commit-with-github-actions-39po" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 6: Test on Commit With Github Actions&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#github&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So this is the structure of our project so far.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; src/
&lt;span class="p"&gt;    -&lt;/span&gt; something/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; py.typed
&lt;span class="p"&gt;        -&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; tests/
&lt;span class="p"&gt;    -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;    -&lt;/span&gt; conftest.py
&lt;span class="p"&gt;    -&lt;/span&gt; test_app.py
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; requirements.txt
&lt;span class="p"&gt;-&lt;/span&gt; setup.cfg
&lt;span class="p"&gt;-&lt;/span&gt; setup.py
&lt;span class="p"&gt;-&lt;/span&gt; tox.ini

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;h3&gt;
  
  
  Table of Contents:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;6.1 - The Need for Github Actions&lt;/li&gt;
&lt;li&gt;6.2 - Configuring Github Actions&lt;/li&gt;
&lt;li&gt;6.3 - Understanding Tox GH Actions&lt;/li&gt;
&lt;li&gt;6.4 - Adding build results to README&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.1 The Need for Github Actions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Everything we did with &lt;code&gt;Tox&lt;/code&gt; is only local to our machine. That is, the tests pass in all the version, type check passes in all the versions and linting works in all the versions as well. However, all of it happens on our machine. That is, on our operating system.&lt;/li&gt;
&lt;li&gt;How do we know that it passes on other machines? Other operating systems?&lt;/li&gt;
&lt;li&gt;Well, this is the “AUTOMATED” part in &lt;code&gt;Automated Testing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Here we will configure &lt;code&gt;Github Actions&lt;/code&gt; to automatically run &lt;code&gt;Tox&lt;/code&gt; every time we make a push to the main branch across different machines (OS).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.2 Configuring Github Actions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To configure &lt;code&gt;Github Actions&lt;/code&gt; we need another configuration file. In this case, an entire directory.&lt;/li&gt;
&lt;li&gt;We need to create the following files and directories: &lt;code&gt;.github/workflows/&amp;lt;name&amp;gt;.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In this case, name can be whatever we want. We will call it, &lt;code&gt;tests.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let’s look at the &lt;code&gt;tests.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.os }}&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;windows-latest&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.6'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.9'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
        &lt;span class="s"&gt;pip install tox tox-gh-actions&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test with tox&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tox&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have the &lt;code&gt;name&lt;/code&gt; field. This is the &lt;code&gt;name&lt;/code&gt; that will show up on our &lt;code&gt;badge&lt;/code&gt; in &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We specify that we want to run our &lt;code&gt;workflow&lt;/code&gt; to run on &lt;code&gt;push&lt;/code&gt; or &lt;code&gt;pull_request&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We only have one job. We call it &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;strategy-&amp;gt;matrix&lt;/code&gt; is a syntax we use in order to create all combinations of environments that we want to use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We have:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;windows-latest&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.6'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.9'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So there will be 8 virtual environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ubuntu-latest, 3.6&lt;/li&gt;
&lt;li&gt;ubuntu-latest, 3.7&lt;/li&gt;
&lt;li&gt;ubuntu-latest, 3.8&lt;/li&gt;
&lt;li&gt;ubuntu-latest, 3.9&lt;/li&gt;
&lt;li&gt;windows-latest, 3.6&lt;/li&gt;
&lt;li&gt;windows-latest, 3.7&lt;/li&gt;
&lt;li&gt;windows-latest, 3.8&lt;/li&gt;
&lt;li&gt;windows-latest, 3.9&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All of these values are preconfigured Github options. We cannot write custom names for these sections. We can find the link to all the supported options at the Github Actions documentation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Now, we just define what are all the &lt;code&gt;steps&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We checkout the repository.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We setup the version of Python.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
  &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the dependencies.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
      &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
        &lt;span class="s"&gt;pip install tox tox-gh-actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Run &lt;code&gt;tox&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test with tox&lt;/span&gt;
    &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tox&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the following steps. Whenever we commit and push to Github, we will see a build getting triggered. Github will automatically detect our workflow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can play around the Github UI from there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Finally this is the project structure we have.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- .github/
    - workflows/
        - tests.yml
- src/
    - something/
        - __init__.py
        - py.typed
        - app.py
- tests/
    - __init__.py
    - conftest.py
    - test_app.py
- .gitignore
- LICENSE
- pyproject.toml
- README.md
- requirements.txt
- setup.cfg
- setup.py
- tox.ini
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.3 Understanding Tox GH Actions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Notice in the &lt;code&gt;tests.yml&lt;/code&gt; file that we install &lt;code&gt;tox-gh-actions&lt;/code&gt; in the steps. We need to understand what this is.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
      &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
        &lt;span class="s"&gt;pip install tox tox-gh-actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Github already has its own set of actions that mimics what &lt;code&gt;tox&lt;/code&gt; does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can use &lt;code&gt;tox-gh-actions&lt;/code&gt; alone in our &lt;code&gt;tox.ini&lt;/code&gt; file, if all we wanted to do was push to Github.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;However, we are setting up &lt;code&gt;tox&lt;/code&gt; with different blocks, because we want to run the tests locally and &lt;code&gt;tox-gh-actions&lt;/code&gt; only runs on Github. We cannot run Github Actions locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;tox-gh-actions&lt;/code&gt; is a tool to make &lt;code&gt;tox&lt;/code&gt; and Github Actions work together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to give Github a way to map our &lt;code&gt;Tox&lt;/code&gt; environments with the github environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If we notice, we already have a &lt;code&gt;gh-actions&lt;/code&gt; section on the &lt;code&gt;tox.ini&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[gh-actions]
python =
    3.6: py36, mypy, flake8
    3.7: py37
    3.8: py38
    3.9: py39
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The syntax is as follows:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[gh-actions]
python =
    &amp;lt;github_python_version1&amp;gt;: &amp;lt;tox_env1&amp;gt;, &amp;lt;tox_env2&amp;gt;
        &amp;lt;github_python_version1&amp;gt;: &amp;lt;tox_env3&amp;gt;, &amp;lt;tox_env4&amp;gt;
        ...
    and so on
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All we do is list out the name of the &lt;code&gt;github environment&lt;/code&gt; and map it to the corresponding &lt;code&gt;tox&lt;/code&gt; environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Again, our &lt;code&gt;tox&lt;/code&gt; environment looks something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tox]
minversion = 3.8.0
envlist = py36, py37, py38, py39, flake8, mypy
isolated_build = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So we are basically mapping:&lt;br&gt;
    - &lt;code&gt;3.6&lt;/code&gt; to &lt;code&gt;py36&lt;/code&gt;, &lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;mypy&lt;/code&gt;. This is because if we look at the configuration of these environments in &lt;code&gt;tox.ini&lt;/code&gt;. Their &lt;code&gt;basepython&lt;/code&gt; field is only &lt;code&gt;3.6&lt;/code&gt;. Therefore, we did not map &lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;mypy&lt;/code&gt; to the other versions.&lt;br&gt;
    - &lt;code&gt;3.7&lt;/code&gt; to &lt;code&gt;py37&lt;/code&gt;.&lt;br&gt;
    - &lt;code&gt;3.8&lt;/code&gt; to &lt;code&gt;py38&lt;/code&gt;.&lt;br&gt;
    - &lt;code&gt;3.9&lt;/code&gt; to &lt;code&gt;py39&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.3 Adding build results to README &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We can add the results of the build in our &lt;code&gt;[README.md](http://README.md)&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;We can do that by adding the following line on our &lt;code&gt;README.md&lt;/code&gt;: &lt;code&gt;![Tests](https://github.com/&amp;lt;Username&amp;gt;/&amp;lt;ProjectName&amp;gt;/actions/workflows/tests.yml/badge.svg)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So our &lt;code&gt;[README.md](http://README.md)&lt;/code&gt; file looks something like this.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Something&lt;/span&gt;
A starter project to show how to set up and use automated testing in Python

&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Tests&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/DummyUser/DummyProject/actions/workflows/tests.yml/badge.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So thats our article. Thank you for making it this far. To understand this article visually, checkout the video mentioned in the credits section.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>testing</category>
      <category>python</category>
      <category>git</category>
    </item>
    <item>
      <title>Chapter 5: Testing Multiple Environments with TOX.</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 12:24:23 +0000</pubDate>
      <link>https://dev.to/zim95/chapter-5-testing-multiple-environments-with-tox-4if0</link>
      <guid>https://dev.to/zim95/chapter-5-testing-multiple-environments-with-tox-4if0</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;This tutorial builds upon the project structure from Chapter 3 and Chapter 4. I suggest understanding Chapter 3 and Chapter 4 before moving forward in order to understand the project structure.

&lt;ul&gt;
&lt;li&gt;Chapter 3: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-3-using-pytest-mypy-and-flake8-for-testing-30cl" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 3: Using PyTest, MyPy and flake8 for testing&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flake8&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Chapter 4: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-4-a-quick-tour-of-pytest-264d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 4: A Quick tour of PyTest&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#unittesting&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;So far, this is the structure of our code.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; src/
&lt;span class="p"&gt;    -&lt;/span&gt; something/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; py.typed
&lt;span class="p"&gt;        -&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; tests/
&lt;span class="p"&gt;    -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;    -&lt;/span&gt; conftest.py
&lt;span class="p"&gt;    -&lt;/span&gt; test_app.py
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; requirements.txt
&lt;span class="p"&gt;-&lt;/span&gt; setup.cfg
&lt;span class="p"&gt;-&lt;/span&gt; setup.py

&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;h3&gt;
  
  
  Table of contents:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;5.1 - The Need for Tox and the Tox configuration file&lt;/li&gt;
&lt;li&gt;5.2 - Understanding the Tox environments and variables&lt;/li&gt;
&lt;li&gt;5.3 - Understanding the flow of Tox environments&lt;/li&gt;
&lt;li&gt;5.4 - Executing TOX&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.1 The Need for Tox &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The code so far has run on our local environment.&lt;/li&gt;
&lt;li&gt;How do we know that the code runs on multiple environments? Since we are building packages, we should know which versions of python can install our package and run it.&lt;/li&gt;
&lt;li&gt;This is where &lt;code&gt;Tox&lt;/code&gt; comes in. To work with &lt;code&gt;Tox&lt;/code&gt; we need a &lt;code&gt;tox.ini&lt;/code&gt; file which is &lt;code&gt;Tox&lt;/code&gt;'s own configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let’s consider the following &lt;code&gt;tox.ini&lt;/code&gt; file in the root directory of the project.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tox]
minversion = 3.8.0
envlist = py36, py37, py38, py39, flake8, mypy
isolated_build = true

[gh-actions]
python =
    3.6: py36, mypy, flake8
    3.7: py37
    3.8: py38
    3.9: py39

[testenv]
setenv =
    PYTHONPATH = {toxinidir}
deps =
    -r{toxinidir}/requirements_dev.txt
commands =
    pytest --basetemp={envtmpdir}

[testenv:flake8]
basepython = python3.6
deps = flake8
commands = flake8 src tests

[testenv:mypy]
basepython = python3.6
deps =
    -r{toxinidir}/requirements_dev.txt
commands = mypy src
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To read more about &lt;code&gt;tox&lt;/code&gt; file structure, we can read: &lt;a href="https://tox.wiki/en/latest/config.html" rel="noopener noreferrer"&gt;https://tox.wiki/en/latest/config.html&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.2 Understanding the Tox environments and variables &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We can read about all of this from &lt;code&gt;[tox.wiki](http://tox.wiki)&lt;/code&gt; ,  &lt;a href="https://tox.wiki/en/latest/config.html" rel="noopener noreferrer"&gt;https://tox.wiki/en/latest/config.html&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All global settings are defined in the &lt;code&gt;[tox]&lt;/code&gt; section. In our case,&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tox]
minversion = 3.8.0
envlist = py36, py37, py38, py39, flake8, mypy
isolated_build = true
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;minversion&lt;/code&gt; is the minimum version of &lt;code&gt;tox-library&lt;/code&gt; required to parse this &lt;code&gt;tox&lt;/code&gt; file.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;3.8.0&lt;/code&gt; is the minimum version of &lt;code&gt;tox-library&lt;/code&gt; required to parse this &lt;code&gt;tox&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;envlist&lt;/code&gt; is for determining the environment list that &lt;code&gt;tox&lt;/code&gt;is to operate on and so on.&lt;/li&gt;

&lt;li&gt;There are many other global setting variables that are available in the &lt;code&gt;&lt;a href="http://tox.wiki" rel="noopener noreferrer"&gt;tox.wiki&lt;/a&gt;&lt;/code&gt; website. We do not need to understand everything as of now.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Test environments are defined under the &lt;code&gt;testenv&lt;/code&gt;section and individual &lt;code&gt;testenv:NAME&lt;/code&gt;sections, where &lt;code&gt;NAME&lt;/code&gt;is the name of a specific environment. This section also has a lot of options to set. For our case, we will go through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;basepython&lt;/code&gt;: Name or path to a Python interpreter which will be used for creating the virtual environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deps&lt;/code&gt;: Environment dependencies. Installed usually from requirements file or manually written.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commands&lt;/code&gt;: The commands to be called for testing. Only execute if &lt;strong&gt;&lt;code&gt;[commands_pre](https://tox.wiki/en/latest/config.html#conf-commands_pre)&lt;/code&gt;&lt;/strong&gt; succeeds. &lt;code&gt;commands_pre&lt;/code&gt; is another option we have not talked about.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setenv&lt;/code&gt;: Each line contains a NAME=VALUE environment variable setting which will be used for all test command invocations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;We might also notice &lt;code&gt;{toxinidir}&lt;/code&gt; which is a variable inbuilt in &lt;code&gt;tox&lt;/code&gt;. We can learning more about changing its working directory: &lt;a href="https://stackoverflow.com/questions/52503796/change-tox-workdir" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/52503796/change-tox-workdir&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For now, we just need to understand that: from &lt;code&gt;tox global settings&lt;/code&gt;section from &lt;code&gt;tox&lt;/code&gt; documentation, the &lt;code&gt;.tox&lt;/code&gt; directory which is working dir, is created in directory where &lt;code&gt;tox.ini&lt;/code&gt;is located.&lt;/li&gt;
&lt;li&gt;Turns out &lt;code&gt;tox&lt;/code&gt; also creates a working directory just like &lt;code&gt;git&lt;/code&gt; does.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;We also have &lt;code&gt;{envtmpdir}&lt;/code&gt; which is another variable inbuilt in &lt;code&gt;tox&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{envdir}&lt;/code&gt; basically points to the &lt;code&gt;virtualenv&lt;/code&gt; directory that &lt;code&gt;tox&lt;/code&gt; creates.&lt;/li&gt;
&lt;li&gt;If we do &lt;code&gt;{envdir}/tmp&lt;/code&gt; it points to the &lt;code&gt;tmp&lt;/code&gt; directory inside the &lt;code&gt;virtualenv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is what &lt;code&gt;{envtmpdir}&lt;/code&gt; points to. Basically &lt;code&gt;{envtmpdir} = {envdir}/tmp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It will be cleared each time before the group of test commands is invoked. In our case, we have three group of test commands: a &lt;code&gt;[test]&lt;/code&gt; which is inbuilt in &lt;code&gt;tox&lt;/code&gt; and two &lt;code&gt;test:NAME&lt;/code&gt; which are our custom test groups.&lt;/li&gt;
&lt;li&gt;There are other similar variables: &lt;code&gt;{envlogdir}={envdir/log}&lt;/code&gt;. It defines a directory for logging where &lt;code&gt;tox&lt;/code&gt; will put logs of &lt;code&gt;tool invocation&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;tox-gh-actions&lt;/code&gt;&lt;/strong&gt;is a &lt;code&gt;tox&lt;/code&gt; plugin which helps running &lt;code&gt;tox&lt;/code&gt; on &lt;code&gt;GitHub Actions&lt;/code&gt; with multiple different &lt;code&gt;Python versions&lt;/code&gt; on multiple workers in parallel.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.3 Understanding the flow of Tox environments &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Tox&lt;/code&gt; allows us to create a bunch of different &lt;code&gt;virtual environments&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt; our &lt;code&gt;package&lt;/code&gt; into those &lt;code&gt;environments&lt;/code&gt; and then run the &lt;code&gt;test groups&lt;/code&gt; on each of those &lt;code&gt;environments&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first four &lt;code&gt;py36, py37, py38, py39,&lt;/code&gt; are &lt;code&gt;built-in&lt;/code&gt; versions of &lt;code&gt;python&lt;/code&gt; that &lt;code&gt;tox&lt;/code&gt; already knows about.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tox]
minversion = 3.8.0
envlist = py36, py37, py38, py39, flake8, mypy
isolated_build = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Their configuration goes in the &lt;code&gt;testenv&lt;/code&gt; block and the tests are run in those environments with the following commands.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[testenv]
setenv =
    PYTHONPATH = {toxinidir}
deps =
    -r{toxinidir}/requirements_dev.txt
commands =
    pytest --basetemp={envtmpdir}
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;We set the &lt;code&gt;PYTHONPATH&lt;/code&gt; to &lt;code&gt;{toxinidir}&lt;/code&gt;. From the previous section we know it equals to &lt;code&gt;PYTHONPATH=&amp;lt;top level of project directory&amp;gt;&lt;/code&gt;. Since, we know &lt;code&gt;toxinidir&lt;/code&gt; points to wherever the &lt;code&gt;tox.ini&lt;/code&gt; file is located. In our case, it is located at the top level of the project directory.&lt;/li&gt;
&lt;li&gt;As you can see, in &lt;code&gt;testenv&lt;/code&gt; , we have set &lt;code&gt;deps=-r{toxinidir}/requirements_dev.txt&lt;/code&gt;. From the previous section we know it equals to &lt;code&gt;deps=-r &amp;lt;project_directory&amp;gt;/requirements_dev.txt&lt;/code&gt;. Since, we know &lt;code&gt;toxinidir&lt;/code&gt; points to wherever the &lt;code&gt;tox.ini&lt;/code&gt; file is located. In our case, it is located at the top level of the project directory.&lt;/li&gt;
&lt;li&gt;We install the requirements and then run the &lt;code&gt;pytest --basetemp=&amp;lt;virtualenv&amp;gt;/tmp&lt;/code&gt; command in all of the environments passed along. Since, we know that &lt;code&gt;{envtmpdir}&lt;/code&gt; points to &lt;code&gt;&amp;lt;virtualenv&amp;gt;/tmp&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Then we have the last two environments: &lt;code&gt;flake8&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tox]
minversion = 3.8.0
envlist = py36, py37, py38, py39, flake8, mypy
isolated_build = true
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;mypy&lt;/code&gt; are not &lt;code&gt;built-in&lt;/code&gt; environments in &lt;code&gt;tox&lt;/code&gt;. Therefore, we have to create separate &lt;code&gt;testenv:NAME&lt;/code&gt; block for them. Which is why we added:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[testenv:flake8]
basepython = python3.6
deps = flake8
commands = flake8 src tests

[testenv:mypy]
basepython = python3.6
deps =
    -r{toxinidir}/requirements_dev.txt
commands = mypy src
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Here, &lt;code&gt;testenv:flake8&lt;/code&gt; is the &lt;code&gt;flake8&lt;/code&gt; virtual environment and &lt;code&gt;testenv:mypy&lt;/code&gt; is the &lt;code&gt;mypy&lt;/code&gt; virtual environment.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Since &lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;mypy&lt;/code&gt; are not versions of python and only commands that we want to run, we need to specify which version of python we want to run on. We do this on &lt;code&gt;basepython&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;deps&lt;/code&gt; again are all the dependencies. We can specify individual dependencies or we can specify the requirements file.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;commands&lt;/code&gt; is the actual command that runs in these blocks.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;We also have, &lt;code&gt;gh-actions&lt;/code&gt; block which we will discuss in the next chapter.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.4 Executing TOX &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Once we have the &lt;code&gt;tox.ini&lt;/code&gt; file and the &lt;code&gt;tox&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;To get the &lt;code&gt;tox&lt;/code&gt; command, &lt;code&gt;pip install tox&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can now just run the &lt;code&gt;tox&lt;/code&gt; command and everything will be automated.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tox
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When we run it, we notice that it takes very long. Why is that?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When we run the tests locally, it runs on our virtual environment.&lt;/li&gt;
&lt;li&gt;But like we discussed, &lt;code&gt;tox&lt;/code&gt; creates a new virtual environment, install everything into that and run tests in all of those environments.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;It is advised to run &lt;code&gt;tox&lt;/code&gt; only before commit and push. Otherwise, just use &lt;code&gt;pytest&lt;/code&gt; , to save time during development.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>python</category>
      <category>pytest</category>
      <category>tox</category>
      <category>testing</category>
    </item>
    <item>
      <title>Chapter 4: A Quick tour of PyTest</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 12:14:53 +0000</pubDate>
      <link>https://dev.to/zim95/chapter-4-a-quick-tour-of-pytest-264d</link>
      <guid>https://dev.to/zim95/chapter-4-a-quick-tour-of-pytest-264d</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;In this section, we will be going through the features of pytest.&lt;/li&gt;
&lt;li&gt;Table of Contents:

&lt;ul&gt;
&lt;li&gt;4.1 - Test Discovery&lt;/li&gt;
&lt;li&gt;4.2 - Parameterising our Tests with &lt;code&gt;@pytest.mark.parameterize&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;4.3 - Skipping Tests&lt;/li&gt;
&lt;li&gt;4.4 - Handling failing test cases&lt;/li&gt;
&lt;li&gt;4.5 - Handling tests that raise errors&lt;/li&gt;
&lt;li&gt;
4.6 - &lt;code&gt;fixtures&lt;/code&gt; in &lt;code&gt;pytest&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;4.6.1 - Understanding &lt;code&gt;fixture&lt;/code&gt; scopes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
4.7 - &lt;code&gt;monkeypatching&lt;/code&gt; (The correct way to mock code)

&lt;ul&gt;
&lt;li&gt;4.7.1 - Understanding the need to mock&lt;/li&gt;
&lt;li&gt;4.7.2 - Monkeypatch syntax overview&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;4.8 - Understanding Test Coverage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.1 Test Discover &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Test discovery is the process of automatically discovering tests within the specified locations.&lt;/li&gt;
&lt;li&gt;There are a lot more use cases of test discovery than whats mentioned in this article. We can specify the configurations on a much more granular level.&lt;/li&gt;
&lt;li&gt;However, we are going to look at the default discovery mechanism that we currently have in &lt;code&gt;pytest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The default implementation is as follows:

&lt;ul&gt;
&lt;li&gt;It looks at the tests in our &lt;code&gt;tests/&lt;/code&gt; directory. We specified this directory in the &lt;code&gt;tool.pytest.ini&lt;/code&gt; option in &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;It then looks for any module (filename in this case) that starts with &lt;code&gt;test_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Within each module it looks for functions with &lt;code&gt;test_&lt;/code&gt;, it assumes that these are the tests and it runs them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;To know more about &lt;code&gt;pytest&lt;/code&gt; test discovery, we can read the documentation.

&lt;ul&gt;
&lt;li&gt;Here is a small article: &lt;a href="https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery"&gt;https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Here is an article on how to change the standard test discovery: &lt;a href="https://docs.pytest.org/en/7.1.x/example/pythoncollection.html"&gt;https://docs.pytest.org/en/7.1.x/example/pythoncollection.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2 Parameterising our Tests &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For now, our code has a single test function inside &lt;code&gt;test_app.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_something&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;somethingelse1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;somethingelse2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;somethingelse3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;somethingelse1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'some'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;somethingelse2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'thing'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;somethingelse3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This test does not indicate the behaviour that we are testing. But later on, we will have multiple tests based on the different tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not only that, notice that in the tests, if one &lt;code&gt;assert&lt;/code&gt; statement fails. Then the rest of the &lt;code&gt;assert&lt;/code&gt; statements are never executed. The test is considered to have failed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In order to prevent this from happening, &lt;code&gt;pytest&lt;/code&gt; allows &lt;code&gt;parameterization&lt;/code&gt; of &lt;code&gt;tests&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It has a &lt;code&gt;@pytest.mark.parameterize&lt;/code&gt; decorator using which we can plug in different values for the same use case (same test function).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This allows our tests to run multiple &lt;code&gt;assert&lt;/code&gt; statements. If one fails, the others will still run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using this our test could look something like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SomethingElse&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameterize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dummy_somethingelse_value, dummy_expected_result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'some'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'thing'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dummy_somethingelse_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dummy_expected_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;do_something_with_somethingelse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomethingElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dummy_expected_result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; \
        &lt;span class="n"&gt;dummy_expected_result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The syntax for &lt;code&gt;parameterize&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;@pytest.mark.parameterize('label1, label2, ... and so on', [
    (label1value, label2value, ... and so on),
    (label1value, label2value, ... and so on),
    ...
    and so on
])
def test_dummy(label1, label2, .... and so on) -&amp;gt; None:
    pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So the main idea behind &lt;code&gt;mark.parameterize&lt;/code&gt; is that if one of the cases in &lt;code&gt;parameterize&lt;/code&gt; list fails, it still runs the rest of them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apart from that, it makes our tests look a lot cleaner.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 Skipping Tests &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We can skip tests in &lt;code&gt;pytest&lt;/code&gt; by using the &lt;code&gt;@pytest.mark.skip(reason='Any message')&lt;/code&gt; decorator.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Feature not implemented yet'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_skip_sometest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;some_uninmplemted_feature&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When we do &lt;code&gt;TDD (Test Driven Development)&lt;/code&gt;, we write tests before we implement the functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We usually skip such tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is also another &lt;code&gt;skipif&lt;/code&gt; variation of skip which lets us skip based on conditions. For example, if the operating system is Windows, or the version of Python is not supported, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.4 Handling Failing Tests &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We might have tests that are bound to fail. These tests can be used as:

&lt;ul&gt;
&lt;li&gt;Tests to assure that things that are supposed to fail (like some virtual scenario), do fail.&lt;/li&gt;
&lt;li&gt;Documentation to better understand the system. To show that certain settings will fail.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;These tests when executed will show up as failure and fail the entire build.&lt;/li&gt;
&lt;li&gt;We can avoid that by deliberately marking them as tests that are supposed to fail.&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;We can achieve this with the &lt;code&gt;pytest.mark.xfail&lt;/code&gt; decorator.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xfail&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_zero_division&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;However, we should not mark those tests that raise errors as failing tests. This brings us to the next topic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.5 Handling Tests that raise Exceptions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We might want to make sure certain scenarios raise a certain exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This can be handled by using the context manager called &lt;code&gt;pytest.raises(Exception)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zero_division&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_zero_division&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;zero_division&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As we can see, there is an exception expected when we run &lt;code&gt;zero_division&lt;/code&gt;. We can handle it with &lt;code&gt;pytest.raises&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This test will now fail if &lt;code&gt;ZeroDivisionError&lt;/code&gt; is not raised.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.6 &lt;code&gt;fixtures&lt;/code&gt; in &lt;code&gt;pytest&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fixtures are used when multiple tests require a certain amount of setup that they all share in common.&lt;/li&gt;
&lt;li&gt;For example, tests that require a database connection.

&lt;ul&gt;
&lt;li&gt;In such cases, it is better to create the connection and share it with tests.&lt;/li&gt;
&lt;li&gt;Rather than creating a connection for every test.&lt;/li&gt;
&lt;li&gt;So we can create &lt;code&gt;Fixtures&lt;/code&gt; that get passed to our tests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;unittest&lt;/code&gt; this is done by the &lt;code&gt;setUp&lt;/code&gt; method. It creates objects that can be shared throughout the test methods.&lt;/li&gt;
&lt;li&gt;By convention if we create a file called &lt;code&gt;conftest.py&lt;/code&gt;, and create a fixture in that file, it will be available for all of our tests based on the&lt;code&gt;scope&lt;/code&gt; of the fixture.&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;So we create &lt;code&gt;conftest.py&lt;/code&gt; in our &lt;code&gt;tests/&lt;/code&gt; directory alongside &lt;code&gt;test_app.py&lt;/code&gt; and create fixtures there as follows:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;db_con&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;db_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"driver+dialect://username:password@host:port/database"&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DatabaseLibrary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notice we have scopes in &lt;code&gt;pytest&lt;/code&gt;. We will look at what scopes are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixture scopes basically define how many times a fixture will run based on the area it covers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.6.1 Understanding &lt;code&gt;fixture&lt;/code&gt; scopes &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To learn more about &lt;code&gt;fixture&lt;/code&gt; &lt;code&gt;scope&lt;/code&gt;, we can go: &lt;a href="https://betterprogramming.pub/understand-5-scopes-of-pytest-fixtures-1b607b5c19ed"&gt;https://betterprogramming.pub/understand-5-scopes-of-pytest-fixtures-1b607b5c19ed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Scope basically has to do with the scope of the fixture as in, upto where the fixture is shared. There are five types of scopes: &lt;code&gt;function&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;module&lt;/code&gt;, &lt;code&gt;package&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;function&lt;/code&gt; scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the default scope without explicitly adding &lt;code&gt;scope='function'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The fixture will be executed per test function.&lt;/li&gt;
&lt;li&gt;This is very heavy task.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This fixture is suitable for single use functions.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;only_used_once&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This fixture is suitable when it handles very lightweight operations such as returning a constant or a different value every time.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;light_operation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"I'm a constant"&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;need_different_value_each_time&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;class&lt;/code&gt; scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This scope runs the fixture per test class.&lt;/li&gt;
&lt;li&gt;If we have couple of test functions that do similar things, we can put them in the same class.&lt;/li&gt;
&lt;li&gt;To know more about grouping tests in classes.

&lt;ul&gt;
&lt;li&gt;Conventions for Python Test Discovery: &lt;a href="https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery"&gt;https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Group multiple tests in a class: &lt;a href="https://docs.pytest.org/en/stable/getting-started.html#group-multiple-tests-in-a-class"&gt;https://docs.pytest.org/en/stable/getting-started.html#group-multiple-tests-in-a-class&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The parent StackOverFlow thread: &lt;a href="https://stackoverflow.com/questions/20277058/py-test-does-not-find-tests-under-a-class#:~:text=test%20does%20not%20find%20tests%20under%20a%20class,-Ask%20Question"&gt;https://stackoverflow.com/questions/20277058/py-test-does-not-find-tests-under-a-class#:~:text=test does not find tests under a class,-Ask Question&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to test &lt;code&gt;unittest.TestCase&lt;/code&gt; based classes: &lt;a href="https://docs.pytest.org/en/7.1.x/how-to/unittest.html"&gt;https://docs.pytest.org/en/7.1.x/how-to/unittest.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Changing Python standard test discovery: &lt;a href="https://docs.pytest.org/en/7.1.x/example/pythoncollection.html"&gt;https://docs.pytest.org/en/7.1.x/example/pythoncollection.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Now that we know how to group tests in classes and test discovery for classes, we will see how we can use fixtures on these test classes with &lt;code&gt;scope="class"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;We can use &lt;code&gt;@pytest.mark.usefixtures("fixturename")&lt;/code&gt; decorator with the test class.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dummy_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Execute fixture"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usefixtures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dummy_data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCalculatorClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test distance function"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_sum_of_square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test sum of square function"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sum_of_square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The fixture will be executed once per test class before the test methods.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;There is a special usage of &lt;code&gt;yield&lt;/code&gt; statement in &lt;code&gt;pytest&lt;/code&gt; that allows us to run the fixture after all the test functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code before &lt;code&gt;yield&lt;/code&gt; acts as &lt;code&gt;setup code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The code after &lt;code&gt;yeild&lt;/code&gt; acts as &lt;code&gt;teardown code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For example, testing a database.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# pseudo code
&lt;/span&gt;    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usefixtures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prepare_db"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestDBClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_query1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_query2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can &lt;code&gt;yield&lt;/code&gt; values to any test that wants it and the remaining code will act as the &lt;code&gt;teardown code&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;module and package&lt;/code&gt; scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;scope='module'&lt;/code&gt; runs the fixture per module (per file).

&lt;ul&gt;
&lt;li&gt;A module may contain multiple functions as well as classes.&lt;/li&gt;
&lt;li&gt;No matter how many tests are in the module, the fixture is run only once.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;scope='package'&lt;/code&gt; runs the fixture per package (directory).

&lt;ul&gt;
&lt;li&gt;A package contains one or more modules.&lt;/li&gt;
&lt;li&gt;No matter how many modules there are, the fixture is run only once.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;An example would be:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_config&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read config"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We might want to read config only once per module or only once throughout the entire package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;session&lt;/code&gt; scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every time we run &lt;code&gt;pytest&lt;/code&gt;, it is considered to be one session.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;scope='session'&lt;/code&gt; makes sure the fixture only executes per session.&lt;/li&gt;
&lt;li&gt;Session scopes are designed for expensive operations like truncating a table or loading a test set to a database.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can read more about fixture topics such as &lt;code&gt;autouse&lt;/code&gt; and execution order of fixtures. We will look at them when required.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.7 Monkey Patching in PyTest &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Monkey patching&lt;/code&gt; is the process of changing the declaration of code during runtime.&lt;/li&gt;
&lt;li&gt;This is &lt;code&gt;mocking&lt;/code&gt; done right.&lt;/li&gt;
&lt;li&gt;To understand &lt;code&gt;mocking&lt;/code&gt; and &lt;code&gt;test-driven development&lt;/code&gt; in &lt;code&gt;Python&lt;/code&gt;, you can check out my tutorial series on &lt;code&gt;TDD in Python&lt;/code&gt;: &lt;a href="https://www.youtube.com/watch?v=NAjCDS-qxrQ&amp;amp;list=PLVWk3kKbCAAuPPr_eh4KNKdL2hDSNFH3H"&gt;https://www.youtube.com/watch?v=NAjCDS-qxrQ&amp;amp;list=PLVWk3kKbCAAuPPr_eh4KNKdL2hDSNFH3H&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TLDW(Too long didn’t watch?)&lt;/code&gt;: Mocking lets us replace methods with dummy objects during runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.7.1 Understanding the need to mock &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Why would we want to mock anything?&lt;/li&gt;
&lt;li&gt;To understand this you need to understand the principals of unit testing:

&lt;ul&gt;
&lt;li&gt;Unit testing are the fastest tests. Why?&lt;/li&gt;
&lt;li&gt;It is testing the functionality of a single unit, without any external dependencies.&lt;/li&gt;
&lt;li&gt;Say you test a function which calls the database.&lt;/li&gt;
&lt;li&gt;You would only want to test the flow of such a function without making the call to the actual database.&lt;/li&gt;
&lt;li&gt;How do you do that? You basically, &lt;code&gt;mock&lt;/code&gt; the entire database connection object and replace it with a dummy object.&lt;/li&gt;
&lt;li&gt;You can set whatever values you want that dummy object to return, create whatever dummy functions are required along with their return types. You can mimic the entire database connection’s set of methods if you want.&lt;/li&gt;
&lt;li&gt;What this allows us to do is set dummy values for all the external calls that are within our function and set default results for them.&lt;/li&gt;
&lt;li&gt;With this we can setup a fake scenario without actually making external calls.&lt;/li&gt;
&lt;li&gt;This is why unit tests are fast.&lt;/li&gt;
&lt;li&gt;Integration testing is on the other hand is testing of the actual values in which case, we would test the actual results from the external calls and evaluate them. Thats why Integration tests are slower than unit tests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;This is one of the reasons we use mock.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Monkey patching&lt;/code&gt; gives us a better interface than barebones &lt;code&gt;mock&lt;/code&gt; library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.7.2 Monkeypatch syntax overview &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Fixtures&lt;/code&gt; can also depend on other &lt;code&gt;fixtures&lt;/code&gt;. &lt;code&gt;monkeypatching&lt;/code&gt; is such a fixture which is available to all functions.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let’s create a &lt;code&gt;fixture&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;capture_stdout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;std_output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'write_count'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fake_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;std_output&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
        &lt;span class="n"&gt;std_output&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'write_count'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std_output&lt;/span&gt;

    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'write'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fake_writer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std_output&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capture_stdout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;capture_stdout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'output'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What we did here is replace &lt;code&gt;sys.stdout.write&lt;/code&gt; with &lt;code&gt;fake_writer&lt;/code&gt; which returns the captured &lt;code&gt;dictionary&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every time a test function uses &lt;code&gt;capture_stdout&lt;/code&gt;  fixture, the environment within the test changes where &lt;code&gt;sys.stdout.write&lt;/code&gt; is replaced with &lt;code&gt;fake_writer&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whenever any function calls &lt;code&gt;sys.stdout&lt;/code&gt; within the function scope will be making a call to &lt;code&gt;fake_writer&lt;/code&gt; instead. In our example, we call &lt;code&gt;print&lt;/code&gt; will calls to &lt;code&gt;sys.stdout&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The return value is captured in the &lt;code&gt;capture_stdout&lt;/code&gt; variable and can be used to &lt;code&gt;assert&lt;/code&gt; results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is how we &lt;code&gt;mock&lt;/code&gt; things.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;But why use &lt;code&gt;monkeypatching&lt;/code&gt; instead of just mocks?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the end of the test, &lt;code&gt;monkeypatching&lt;/code&gt; makes sure that everything is undone and &lt;code&gt;sys.out.writer&lt;/code&gt; goes back to its original value.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;unittest&lt;/code&gt; we use &lt;code&gt;mock.patch&lt;/code&gt; as &lt;code&gt;decorators&lt;/code&gt; and &lt;code&gt;context managers&lt;/code&gt;. This also  makes sure that everything is undone at the end of the test.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.8 UNDERSTANDING TEST COVERAGE &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We have put &lt;code&gt;--cov&lt;/code&gt; options in &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This shows us test coverage in percentages.&lt;/li&gt;
&lt;li&gt;Here &lt;code&gt;100%&lt;/code&gt; test coverage means all our tests combined and touched every single line of executable code in the &lt;code&gt;src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any percentage lesser means that some executable code was not tested by our tests.&lt;/li&gt;
&lt;li&gt;However, having &lt;code&gt;100%&lt;/code&gt; test coverage does denote that there are no bugs. It all depends. However, it’s good to have &lt;code&gt;100%&lt;/code&gt; code coverage.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>pytest</category>
      <category>testing</category>
      <category>unittesting</category>
    </item>
    <item>
      <title>Chapter 3: Using PyTest, MyPy and flake8 for testing</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 12:06:29 +0000</pubDate>
      <link>https://dev.to/zim95/chapter-3-using-pytest-mypy-and-flake8-for-testing-30cl</link>
      <guid>https://dev.to/zim95/chapter-3-using-pytest-mypy-and-flake8-for-testing-30cl</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Please consider reading the previous chapter (Chapter 2) before moving forward. This chapter builds upon the project structure discussed in the previous chapter (Chapter 2).
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122337%2F81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-2-the-need-for-a-project-structure-82h" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 2: The Need Of A Project Structure In Python Testing&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the previous chapter (Chapter 2), we have the following project structure:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;    -&lt;/span&gt; src/
&lt;span class="p"&gt;    -&lt;/span&gt; something/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; app.py
&lt;span class="p"&gt;    -&lt;/span&gt; tests/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; test_app.py
&lt;span class="p"&gt;    -&lt;/span&gt; .gitignore
&lt;span class="p"&gt;    -&lt;/span&gt; LICENSE
&lt;span class="p"&gt;    -&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;    -&lt;/span&gt; README.md
&lt;span class="p"&gt;    -&lt;/span&gt; requirements.txt
&lt;span class="p"&gt;    -&lt;/span&gt; setup.cfg
&lt;span class="p"&gt;    -&lt;/span&gt; setup.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here we are going to setup &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt; and &lt;code&gt;flake8&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;h3&gt;
  
  
  TABLE OF CONTENTS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;3.1 - Adding new dependencies to requirements&lt;/li&gt;
&lt;li&gt;3.2 - Adding to our metadata in setup.cfg&lt;/li&gt;
&lt;li&gt;3.3 - Adding python configurations to pyproject.toml&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.1 Adding new dependencies to requirements &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Now we need to install more libraries. Namely &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt; and &lt;code&gt;flake8&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So our &lt;code&gt;requirements.txt&lt;/code&gt; file looks something like this. We have added dependencies for &lt;code&gt;pytest&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flake==3.9.4
tox==3.24.3
pytest==6.2.5
pytest-cov==2.12.1
mypy==0.910
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of directly overwriting the &lt;code&gt;requirements.txt&lt;/code&gt; file, we create a new file called &lt;code&gt;requirements_dev.txt&lt;/code&gt;. Since testing is required in &lt;code&gt;dev&lt;/code&gt; environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So we have two requirement files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; which we use while running our code:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests==2.26.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;requirements_dev.txt&lt;/code&gt; which holds dev dependencies like our test, lint and type check libraries.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flake==3.9.4
tox==3.24.3
pytest==6.2.5
pytest-cov==2.12.1
mypy==0.910
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;While running tests we will use &lt;code&gt;requirements_dev.txt&lt;/code&gt; file to install dependencies.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Adding to our metadata in &lt;code&gt;setup.cfg&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Our &lt;code&gt;setup.cfg&lt;/code&gt; file looks something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    [metadata]
    name = something
    description = just some dummy codebase
    author = Coding with Zim
    license = MIT
    license_file = LICENSE
    platforms = unix, linux, osx, cygwin, win32
    classifiers = 
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3 :: Only
        Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
        Programming Language :: Python :: 3.8
        Programming Language :: Python :: 3.9

    [options]
    packages =
        something
    install_requires =
        requests&amp;gt;=2
    python_requires = &amp;gt;=3.6
    package_dir =
        =src
    zip_safe = no

    [options.extras_require]
    testing=
        pytest&amp;gt;=6.0
        pytest-cov&amp;gt;=2.0
        mypy&amp;gt;=0.910
        flake8&amp;gt;=3.9
        tox&amp;gt;=3.24

    [options.package_data]
    something = py.typed

    [flake8]
    max-line-length = 160
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;options.extras_require&lt;/code&gt; are optional dependencies. We can read more about it here: &lt;a href="https://setuptools.pypa.io/en/latest/userguide/dependency_management.html" rel="noopener noreferrer"&gt;https://setuptools.pypa.io/en/latest/userguide/dependency_management.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;options.package_data&lt;/code&gt; is a type of &lt;code&gt;Data File Support&lt;/code&gt; , we can read more about it here: &lt;a href="https://setuptools.pypa.io/en/latest/userguide/datafiles.html" rel="noopener noreferrer"&gt;https://setuptools.pypa.io/en/latest/userguide/datafiles.html&lt;/a&gt;. We are basically using it to denote that our code is &lt;code&gt;type-hinted&lt;/code&gt;, which means we are using types in the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For this to work we need to create a &lt;code&gt;py.typed&lt;/code&gt; blank file in our something directory alongside &lt;code&gt;__init__.py&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;flake8&lt;/code&gt; is the configuration for the &lt;code&gt;flake8&lt;/code&gt; linter that we will be using. Here we are only saying that the maximum line length is 160.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So our &lt;code&gt;flake8&lt;/code&gt; configuration is stored in the &lt;code&gt;cfg&lt;/code&gt; file and our &lt;code&gt;python&lt;/code&gt; configuration is stored in our &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Adding python configurations to &lt;code&gt;pyproject.toml&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Our &lt;code&gt;pyproject.toml&lt;/code&gt; file looks something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;["setuptools&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;42.0&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="err"&gt;wheel&lt;/span&gt;&lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"setuptools.build_meta"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.pytest.ini_options]&lt;/span&gt;
&lt;span class="py"&gt;addopts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"--cov&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;something&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;testpaths&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"tests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.mypy]&lt;/span&gt;
&lt;span class="py"&gt;mypy_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src"&lt;/span&gt;
&lt;span class="py"&gt;check_untyped_defs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;disallow_any_generics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ignore_missing_imports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;no_implicit_optional&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;show_error_codes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;strict_equality&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;warn_redundant_casts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;warn_return_any&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;warn_unreachable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;warn_unused_configs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;no_implicit_reexport&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We already discussed about the &lt;code&gt;build-system&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;tool.pytest.ini_options&lt;/code&gt; are &lt;code&gt;pytest&lt;/code&gt; tool options during the execution of tests. In our case we have added the &lt;code&gt;--cov=something&lt;/code&gt; option in &lt;code&gt;addopts&lt;/code&gt; so that we see the test coverage. &lt;code&gt;testpaths&lt;/code&gt; basically denotes all the directories containing tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;tool.mypy&lt;/code&gt; are &lt;code&gt;mypy&lt;/code&gt; tool options. To learn about &lt;code&gt;mypy&lt;/code&gt;: &lt;a href="https://realpython.com/lessons/type-checking-mypy/#:~:text=%E2%80%9CMypy%20is%20an%20optional%20static,background%20on%20the%20Mypy%20project" rel="noopener noreferrer"&gt;https://realpython.com/lessons/type-checking-mypy/#:~:text=“Mypy is an optional static,background on the Mypy project&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So, now we install the &lt;code&gt;dev&lt;/code&gt; dependencies with &lt;code&gt;pip&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;yourproject&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements_dev.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally we should be able to run our test with &lt;code&gt;pytest&lt;/code&gt;, check linting with &lt;code&gt;flake8&lt;/code&gt; and type check with &lt;code&gt;mypy&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pytest &lt;span class="c"&gt;# this command will test our code with default test discovery mechanism.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;mypy &lt;span class="c"&gt;# this command will run type checks on our code with mypy.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;flake8 &lt;span class="c"&gt;# this command will check code linting.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>pytest</category>
      <category>flake8</category>
    </item>
    <item>
      <title>Chapter 2: The Need Of A Project Structure In Python Testing</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 11:04:02 +0000</pubDate>
      <link>https://dev.to/zim95/chapter-2-the-need-for-a-project-structure-82h</link>
      <guid>https://dev.to/zim95/chapter-2-the-need-for-a-project-structure-82h</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Please consider reading the previous chapter (Chapter 1) before moving forward. This chapter builds upon the project structure discussed in the previous chapter (Chapter 1).
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/the-need-for-test-automation-1952" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 1: The Need For Test Automation&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the previous chapter (Chapter 1), we have the following project structure:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; test_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We would want separate &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;tests&lt;/code&gt; directories. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;test_app.py&lt;/code&gt; we are picking the &lt;code&gt;pytest&lt;/code&gt; solution mentioned in the previous chapter (Chapter1). Because it is a easier to use compared to other testing libraries or frameworks:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5*(4+5)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# test addition and multiplication
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"10 - (100/2)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="c1"&gt;# test subtraction and division. 
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' + 'b'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'ab'&lt;/span&gt; &lt;span class="c1"&gt;# test string concatenation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;h3&gt;
  
  
  Table of Contents:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2.1 - Understanding the problem with import statements&lt;/li&gt;
&lt;li&gt;2.2 - Solution to the problem with import statements&lt;/li&gt;
&lt;li&gt;
2.3 - Making our application installable

&lt;ul&gt;
&lt;li&gt;2.3.1 - Understanding the &lt;code&gt;pyproject.toml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;2.3.2 - Understanding the &lt;code&gt;setup.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;2.3.3 - Understanding the &lt;code&gt;setup.cfg&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.1 Understanding the problem with import statements. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Our test file looks something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5*(4+5)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# test addition and multiplication
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"10 - (100/2)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="c1"&gt;# test subtraction and division. 
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' + 'b'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'ab'&lt;/span&gt; &lt;span class="c1"&gt;# test string concatenation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The import statement &lt;code&gt;from app import simple_calculator_function&lt;/code&gt; assumes that &lt;code&gt;app.py&lt;/code&gt; is on the same folder as the &lt;code&gt;test_app.py&lt;/code&gt; and therefore this kind of import works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We do not want to depend on this feature for tests. We want our tests to run no matter where they are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here, to make sure that the import works, we would need to make sure that &lt;code&gt;test_app.py&lt;/code&gt; and &lt;code&gt;app.py&lt;/code&gt; are in the same directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In case they are in different directories, we need to make sure we run both tests and the application from a common working directory. This working directory is outside both &lt;code&gt;tests&lt;/code&gt; and &lt;code&gt;src&lt;/code&gt; directories&lt;br&gt;
In this case, the import statement in the &lt;code&gt;test_app.py&lt;/code&gt; file would be:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;common_working_directory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;app_directory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simple_calculator_function.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;In this case, the tests also need to be run from the &lt;code&gt;common_working_directory&lt;/code&gt;. The test directory would be: &lt;code&gt;&amp;lt;common_working_directory&amp;gt;/tests/test_*.py&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This stops us from running the tests from anywhere and now we have to depend on having the same working directory as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to make sure that import works from anywhere and doesn't depend on the directory structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The way to make that happen is to make your project an &lt;code&gt;installable&lt;/code&gt; project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 Solution to the problem with import statements. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The solution to the import problem is to turn your project into an &lt;code&gt;installable&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;We need to setup before applying the solution. This is what we will do in this section.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So lets move on with a new folder structure. We create &lt;code&gt;src/simple_calculator/&lt;/code&gt; and &lt;code&gt;tests/&lt;/code&gt; directories.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; src/
&lt;span class="p"&gt;    -&lt;/span&gt; simple_calculator/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; tests/
&lt;span class="p"&gt;    -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;    -&lt;/span&gt; test_app.py
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;app.py&lt;/code&gt; is placed inside &lt;code&gt;src/simple_calculator/&lt;/code&gt; along with an &lt;code&gt;__init__.py&lt;/code&gt; file and &lt;code&gt;test_app.py&lt;/code&gt; is placed inside &lt;code&gt;tests/&lt;/code&gt; along with an &lt;code&gt;__init__.py&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As soon as we create the new directory we get an import error on the test file when we run it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Unresolved Reference 'app'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;This is because the test file can no longer find &lt;code&gt;app&lt;/code&gt;. They are in different directories.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;To make it find the &lt;code&gt;app&lt;/code&gt;, we can import from the project directory's root and run tests also from the same directory like we mentioned eariler. This would mean the import statement inside &lt;code&gt;test_app.py&lt;/code&gt; would look like:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;src.simple_calculator.app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Like we mentioned earlier, the problem with this is that we need to run the test also from the project directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Again the idea is that would like to run our tests from anywhere without worrying about directory structures for imports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To enable that we can expect our code to function as a library so that imports can happen from anywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;This means making our application &lt;code&gt;installable&lt;/code&gt;. So that the following import works after installing our &lt;code&gt;package&lt;/code&gt; in the &lt;code&gt;virtual environment&lt;/code&gt; with &lt;code&gt;pip&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;simple_calculator.app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now no matter where the test is it can use the same import statement everywhere. This solves the problem of having to depend on directory structure for imports.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3 Making our application installable. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To make our application installable, we need to add a bunch of configuration files.

&lt;ul&gt;
&lt;li&gt;This is an open issue in the Python community.&lt;/li&gt;
&lt;li&gt;The idea is, we shouldn’t need these many files to make our application installable.&lt;/li&gt;
&lt;li&gt;Things could have been easier maybe, but, that is how it is at the moment.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;NOTE: The structure that we will follow using &lt;code&gt;setup.py&lt;/code&gt;, &lt;code&gt;setup.cfg&lt;/code&gt; and &lt;code&gt;pyproject.toml&lt;/code&gt; are not required to be the same always. &lt;code&gt;setup.py&lt;/code&gt; perfectly supports entire functionalities. We can write the entire configurations on any one of these files if we have to. We are just organising our code better.&lt;/li&gt;
&lt;li&gt;If you look at the documentation of &lt;code&gt;setuptools&lt;/code&gt;: &lt;a href="https://setuptools.pypa.io/en/latest/index.html"&gt;&lt;/a&gt;&lt;a href="https://setuptools.pypa.io/en/latest/index.html"&gt;https://setuptools.pypa.io/en/latest/index.html&lt;/a&gt;. Then you’ll see that each and every configuration field in the example files has a counter part in each of the file types.&lt;/li&gt;
&lt;li&gt;The community is pushing the configuration more towards the &lt;code&gt;toml&lt;/code&gt; file and just leaving the metadata in the &lt;code&gt;cfg&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3.1 Understanding the &lt;code&gt;pyproject.toml&lt;/code&gt; file. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The first file we will look at is &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the early days of Python, there was only one way to install packages. We needed a &lt;code&gt;setup.py&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;But nowadays, there are several options such as &lt;code&gt;Poetry&lt;/code&gt; and other such examples.&lt;/li&gt;
&lt;li&gt;We can still stick to using the &lt;code&gt;setup.py&lt;/code&gt; way and that is what we will be doing in this article.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can do this by inserting a &lt;code&gt;build-backend&lt;/code&gt; to our &lt;code&gt;[build-system]&lt;/code&gt; in our &lt;code&gt;pyproject.toml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;["setuptools&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;42.8&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="err"&gt;wheel&lt;/span&gt;&lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"setuptools.build_meta"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have setup &lt;code&gt;build-backed&lt;/code&gt; to use &lt;code&gt;setuptools.build_meta&lt;/code&gt;, this will make our project run code in &lt;code&gt;setup.py&lt;/code&gt;. The &lt;code&gt;build-backend&lt;/code&gt; requires &lt;code&gt;setuptools&lt;/code&gt; and we mention that in the &lt;code&gt;requires&lt;/code&gt; section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So the next file to look at is &lt;code&gt;setup.py&lt;/code&gt;, because our &lt;code&gt;build-backend&lt;/code&gt; is &lt;code&gt;setuptools&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3.2 Understanding the &lt;code&gt;setup.py&lt;/code&gt; file. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next is the &lt;code&gt;setup.py&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;In the early days, &lt;code&gt;setup.py&lt;/code&gt; used to be the place containing the installation script.&lt;/li&gt;
&lt;li&gt;It would do everything required to do in order to install a python package.&lt;/li&gt;
&lt;li&gt;We can run arbitrary code inside &lt;code&gt;setup.py&lt;/code&gt;. Since it is a python script.&lt;/li&gt;
&lt;li&gt;This is seen as a security risk and therefore, more and more code is being stripped out of the &lt;code&gt;setup.py&lt;/code&gt; file and put into one of these other configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let’s create a basic &lt;code&gt;setup.py&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;setuptools&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This basic file is going to allow us to install our package in editable mode. Since, we have mentioned &lt;code&gt;setuptools.build_meta&lt;/code&gt; in &lt;code&gt;build-system&lt;/code&gt; in &lt;code&gt;pyproject.toml&lt;/code&gt;, when we run the install script, &lt;code&gt;setup.py&lt;/code&gt; will be executed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3.3 Understanding the &lt;code&gt;setup.cfg&lt;/code&gt; file. &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To store the metadata of the project such as &lt;code&gt;Title&lt;/code&gt; and &lt;code&gt;Description&lt;/code&gt;, we create a &lt;code&gt;setup.cfg&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The file could look as follows:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[metadata]
name = something
description = just some dummy codebase
author = Coding with Zim
license = MIT
license_file = LICENSE
platforms = unix, linux, osx, cygwin, win32
classifiers = 
    Programming Language :: Python :: 3
    Programming Language :: Python :: 3 :: Only
    Programming Language :: Python :: 3.6
    Programming Language :: Python :: 3.7
    Programming Language :: Python :: 3.8
    Programming Language :: Python :: 3.9

[options]
packages =
    something
install_requires =
    requests&amp;gt;=2
python_requires = &amp;gt;=3.6
package_dir =
    =src
zip_safe = no
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NOTE: In &lt;code&gt;[options]&lt;/code&gt; we have new line after &lt;code&gt;=&lt;/code&gt; sign. This signifies that there can be more than one of these values. So, &lt;code&gt;packages&lt;/code&gt;, &lt;code&gt;install_requires&lt;/code&gt;, &lt;code&gt;package_dir&lt;/code&gt; can have multiple values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;packages&lt;/code&gt; are the names of packages that we are creating.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;install_requires&lt;/code&gt; are the names of requirements. Need to look at how to do this with &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;python_requires&lt;/code&gt; denotes the versions of python supported.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;package_dir&lt;/code&gt; is the directory where our application module lives. Inside the &lt;code&gt;src&lt;/code&gt; directory. There might be other names. We are just going with the naming convention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;zip_safe&lt;/code&gt; is &lt;code&gt;no&lt;/code&gt;. No idea what it is for now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why use this file instead of putting everything in setup.py?&lt;/strong&gt;&lt;br&gt;
Since this is just a configuration file and not a python script, we don’t have to worry about it executing arbitrary code which was the case with &lt;code&gt;setup.py&lt;/code&gt;. This is what the community wants. They want to push everything into different configuration files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then we add a &lt;code&gt;requirements.txt&lt;/code&gt; file which contains all our dependencies.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests==2.26.0
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;In our case, it only contains &lt;code&gt;requests&lt;/code&gt; for demo purposes.&lt;br&gt;
NOTE: In &lt;code&gt;setup.py&lt;/code&gt; we gave a version &lt;code&gt;&amp;gt;=2&lt;/code&gt;. In &lt;code&gt;requirements.txt&lt;/code&gt; , we specify the actual version. That is best practice.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With this our project structure looks something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; src/
&lt;span class="p"&gt;    -&lt;/span&gt; something/
&lt;span class="p"&gt;        -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;        -&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; tests/
&lt;span class="p"&gt;    -&lt;/span&gt; __init__.py
&lt;span class="p"&gt;    -&lt;/span&gt; test_app.py
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; requirements.txt
&lt;span class="p"&gt;-&lt;/span&gt; setup.cfg
&lt;span class="p"&gt;-&lt;/span&gt; setup.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we should be able to install it with &lt;code&gt;pip&lt;/code&gt; by the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;your_project_folder&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="c"&gt;# e means editable i guess.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tox</category>
      <category>testing</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Chapter 1: The Need For Test Automation</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 10:42:07 +0000</pubDate>
      <link>https://dev.to/zim95/the-need-for-test-automation-1952</link>
      <guid>https://dev.to/zim95/the-need-for-test-automation-1952</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This chapter is part of the series: &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/understanding-the-python-tox-ecosystem-3noi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Python Tox Ecosystem&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 2 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;At first, we need to understand, how we can test python code without a testing library.&lt;/li&gt;
&lt;li&gt;We will then look into how to test with an existing library.&lt;/li&gt;
&lt;li&gt;Then we will look at how &lt;code&gt;tox&lt;/code&gt; fits in the ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.1 Testing Without A Library
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Consider the following code structure of a Python application:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We may have various &lt;code&gt;assert statements&lt;/code&gt; written inside &lt;code&gt;app.py&lt;/code&gt;. However, these &lt;code&gt;tests&lt;/code&gt; need to be executed manually by calling the function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consider the following code for &lt;code&gt;app.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evaluation_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;'''
    Reads an evaluation string.
    Evaluates it and returns the result.
    For example, eval("5*(4+5)") = 45.

    params:
        evaluation_string: str: The string that will be evaluated.
    '''&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evaluation_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5*(4+5)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# test addition and multiplication
&lt;/span&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"10 - (100/2)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="c1"&gt;# test subtraction and division. 
&lt;/span&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' + 'b'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'ab'&lt;/span&gt; &lt;span class="c1"&gt;# test string concatenation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;As you can see, &lt;code&gt;test_simple_calculator_function&lt;/code&gt; will not run automatically unless called. We want to automate this.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first step is to create a separate &lt;code&gt;test_file&lt;/code&gt; which uses libraries to write tests. The new project code would be:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; README.md
&lt;span class="p"&gt;-&lt;/span&gt; LICENSE
&lt;span class="p"&gt;-&lt;/span&gt; .gitignore
&lt;span class="p"&gt;-&lt;/span&gt; app.py
&lt;span class="p"&gt;-&lt;/span&gt; test_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have added a &lt;code&gt;test_app.py&lt;/code&gt; which will work on libraries.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.2 USING AN EXISTING TESTING LIBRARY
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We have libraries such as the &lt;code&gt;unittest&lt;/code&gt; library using which we can write test subclasses that are auto-detected and run automatically.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Example &lt;code&gt;unittest&lt;/code&gt; subclass:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5*(4+5)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"10-(100/2)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' + 'b'"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;'ab'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can run this code with &lt;code&gt;unittest&lt;/code&gt; tool in the shell as:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'./'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'test_*.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Here, &lt;code&gt;unittest discover&lt;/code&gt; discovers all the subclasses of &lt;code&gt;unittest.TestCase&lt;/code&gt;, i.e. those classes that have inherited &lt;code&gt;unittest.TestCase&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-s&lt;/code&gt; flag denotes the start directory of where the tests are located. In this case, we can stay in the current working directory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; flag denotes the pattern of the names of test files. In this case, the test file naming pattern is &lt;code&gt;test_*.py&lt;/code&gt; , where &lt;code&gt;*&lt;/code&gt; can be replaced with any name.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We also have the &lt;code&gt;pytest&lt;/code&gt; library which is not inbuilt and needs to be installed separately.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For example, &lt;code&gt;pytest&lt;/code&gt; test file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5*(4+5)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# test addition and multiplication
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"10 - (100/2)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="c1"&gt;# test subtraction and division. 
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;simple_calculator_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' + 'b'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'ab'&lt;/span&gt; &lt;span class="c1"&gt;# test string concatenation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pytest&lt;/code&gt; can autodetect tests. We can just run &lt;code&gt;pytest&lt;/code&gt; in the shell and all tests will run.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The test detection algorithm also known as the test discovery standard is is used to detect tests by default unless external modifications are made.&lt;/li&gt;
&lt;li&gt;There are other testing frameworks such as &lt;code&gt;Nose&lt;/code&gt;, &lt;code&gt;DocTest&lt;/code&gt;, etc. which will not be covered in this article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3 TOX USE CASE IN THE ECOSYSTEM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Tox&lt;/code&gt; is just another automation tool for testing.&lt;/li&gt;
&lt;li&gt;What exactly does it automate?

&lt;ul&gt;
&lt;li&gt;Since we are building a package, we need to make sure that our package runs on different versions of Python.&lt;/li&gt;
&lt;li&gt;The tests that we have in &lt;code&gt;pytest&lt;/code&gt; will run on the local version of the &lt;code&gt;virtualenv&lt;/code&gt; that we are currently on. So, it will only check one version of python.&lt;/li&gt;
&lt;li&gt;We need to make sure these tests run passes on all versions of Python.&lt;/li&gt;
&lt;li&gt;This is what &lt;code&gt;Tox&lt;/code&gt; automates. Multi-version testing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;How does &lt;code&gt;Tox&lt;/code&gt; do this?

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Tox&lt;/code&gt; creates a virtual environment for each version of python and/or the operating system that we specify.&lt;/li&gt;
&lt;li&gt;It runs the tests in all those environments and makes sure that our package works on those versions as well.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;When we run things on &lt;code&gt;github actions&lt;/code&gt; :

&lt;ul&gt;
&lt;li&gt;We need to map &lt;code&gt;Tox&lt;/code&gt; commands to &lt;code&gt;github actions&lt;/code&gt; , essentially because &lt;code&gt;github actions&lt;/code&gt; is completely capable of doing what &lt;code&gt;Tox&lt;/code&gt; is doing.&lt;/li&gt;
&lt;li&gt;We are only installing &lt;code&gt;Tox&lt;/code&gt; to automate testing locally on our machines while developing.&lt;/li&gt;
&lt;li&gt;Since we have a working &lt;code&gt;Tox&lt;/code&gt; file, why create another &lt;code&gt;github action&lt;/code&gt; section for the thing already automated in &lt;code&gt;Tox&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We just map &lt;code&gt;Tox&lt;/code&gt; environments to &lt;code&gt;github action&lt;/code&gt; environments.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tox</category>
      <category>testing</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Understanding the Python Tox Ecosystem</title>
      <dc:creator>Namah Shrestha</dc:creator>
      <pubDate>Fri, 21 Oct 2022 10:25:36 +0000</pubDate>
      <link>https://dev.to/zim95/understanding-the-python-tox-ecosystem-3noi</link>
      <guid>https://dev.to/zim95/understanding-the-python-tox-ecosystem-3noi</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This tutorial will show you how to automate testing for python projects using &lt;code&gt;tox&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you've ever worked on a python package, chances are you have seen files like: &lt;code&gt;setup.cfg&lt;/code&gt;, &lt;code&gt;setup.py&lt;/code&gt;, &lt;code&gt;pyproject.toml&lt;/code&gt;, &lt;code&gt;tox.ini&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This tutorial explains what they are as well as walks you through how you can use them to your benefit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chapter 1: The Need For Test Automation

&lt;ul&gt;
&lt;li&gt;1.1 - Testing Without A library&lt;/li&gt;
&lt;li&gt;1.2 - Using an Existing Testing Library&lt;/li&gt;
&lt;li&gt;1.3 - Understanding the TOX Use Case in the Eco System
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/the-need-for-test-automation-1952" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 1: The Need For Test Automation&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Chapter 2 - The Need of A project Structure in Python Testing

&lt;ul&gt;
&lt;li&gt;2.1 - Understanding the problem with import statements&lt;/li&gt;
&lt;li&gt;2.2 - Solution to the problem with import statements&lt;/li&gt;
&lt;li&gt;2.3 - Making our application installable

&lt;ul&gt;
&lt;li&gt;2.3.1 - Understanding the &lt;code&gt;pyproject.toml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;2.3.2 - Understanding the &lt;code&gt;setup.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;2.3.3 - Understanding the &lt;code&gt;setup.cfg&lt;/code&gt; file &lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-2-the-need-for-a-project-structure-82h" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 2: The Need Of A Project Structure In Python Testing&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 6 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chapter 3 - Using &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt; and &lt;code&gt;flake8&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;3.1 - Adding new dependencies to requirements&lt;/li&gt;
&lt;li&gt;3.2 - Adding to our metadata in &lt;code&gt;setup.cfg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;3.3 - Adding python configurations to &lt;code&gt;pyproject.toml&lt;/code&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-3-using-pytest-mypy-and-flake8-for-testing-30cl" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 3: Using PyTest, MyPy and flake8 for testing&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flake8&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Chapter 4 - A Quick Tour of &lt;code&gt;Pytest&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;4.1 - Test Discovery&lt;/li&gt;
&lt;li&gt;4.2 - Working with parameterising&lt;/li&gt;
&lt;li&gt;4.3 - Skipping Tests&lt;/li&gt;
&lt;li&gt;4.4 - Handling Failing Tests without breaking the build&lt;/li&gt;
&lt;li&gt;4.5 - Handling Tests that raise errors&lt;/li&gt;
&lt;li&gt;4.6 - Working with fixtures

&lt;ul&gt;
&lt;li&gt;4.6.1 - Understanding &lt;code&gt;fixture scopes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;4.7 - Working with monkey patching

&lt;ul&gt;
&lt;li&gt;4.7.1 - Understanding the need to mock&lt;/li&gt;
&lt;li&gt;4.7.2 - Monkeypatch syntax overview&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;4.8 - Understanding test coverage

&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-4-a-quick-tour-of-pytest-264d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 4: A Quick tour of PyTest&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 10 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#unittesting&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chapter 5 - Test multiple environments with &lt;code&gt;Tox&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;5.1 - The Need for Tox and the Tox configuration file&lt;/li&gt;
&lt;li&gt;5.2 - Understanding the Tox variables&lt;/li&gt;
&lt;li&gt;5.3 - Understanding the flow of tox environments&lt;/li&gt;
&lt;li&gt;5.4 - Executing Tox
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-5-testing-multiple-environments-with-tox-4if0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 5: Testing Multiple Environments with TOX.&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tox&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Chapter 6 - Test on commit with Github Actions

&lt;ul&gt;
&lt;li&gt;6.1 - The Need for Github Actions&lt;/li&gt;
&lt;li&gt;6.2 - Configuring Github Actions&lt;/li&gt;
&lt;li&gt;6.3 - Understanding Tox GH Actions&lt;/li&gt;
&lt;li&gt;6.4 - Adding Build Results to README
&lt;div class="ltag__link"&gt;
  &lt;a href="/zim95" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtJhr9w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--TuBUv7KW--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/122337/81c19fe0-bf60-4343-875f-ea8660bf5976.jpeg" alt="zim95"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zim95/chapter-6-test-on-commit-with-github-actions-39po" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chapter 6: Test on Commit With Github Actions&lt;/h2&gt;
      &lt;h3&gt;Namah Shrestha ・ Oct 21 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#github&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;h2&gt;
  
  
  Credits - Study Material
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This article is based on the following youtube video:
&lt;a href="https://www.youtube.com/watch?v=DhUpxWjOhME"&gt;https://www.youtube.com/watch?v=DhUpxWjOhME&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Please check it out. This video offers a visual counterpart to this article. It is the original video this article is based out of.&lt;/li&gt;
&lt;li&gt;Other articles used throughout the article to understand various concepts better:

&lt;ul&gt;
&lt;li&gt;SetupTools Documentation - &lt;a href="https://setuptools.pypa.io/en/latest/index.html"&gt;https://setuptools.pypa.io/en/latest/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Type Checking with MyPy - &lt;a href="https://realpython.com/lessons/type-checking-mypy/#:%7E:text=%E2%80%9CMypy%20is%20an%20optional%20static,background%20on%20the%20Mypy%20project"&gt;https://realpython.com/lessons/type-checking-mypy/#:~:text=%E2%80%9CMypy%20is%20an%20optional%20static,background%20on%20the%20Mypy%20project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Test Discover in PyPy - &lt;a href="https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery"&gt;https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Grouping multiple tests in a class - &lt;a href="https://docs.pytest.org/en/stable/getting-started.html#group-multiple-tests-in-a-class"&gt;https://docs.pytest.org/en/stable/getting-started.html#group-multiple-tests-in-a-class&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Understanding grouping of tests in a class - &lt;a href="https://stackoverflow.com/questions/20277058/py-test-does-not-find-tests-under-a-class#:%7E:text=test%20does%20not%20find%20tests%20under%20a%20class,-Ask%20Question"&gt;https://stackoverflow.com/questions/20277058/py-test-does-not-find-tests-under-a-class#:~:text=test%20does%20not%20find%20tests%20under%20a%20class,-Ask%20Question&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Testing unittest based classes - &lt;a href="https://docs.pytest.org/en/7.1.x/how-to/unittest.html"&gt;https://docs.pytest.org/en/7.1.x/how-to/unittest.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Change the standard in Test Discovery - &lt;a href="https://docs.pytest.org/en/7.1.x/example/pythoncollection.html"&gt;https://docs.pytest.org/en/7.1.x/example/pythoncollection.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fixture Scopes - &lt;a href="https://betterprogramming.pub/understand-5-scopes-of-pytest-fixtures-1b607b5c19ed"&gt;https://betterprogramming.pub/understand-5-scopes-of-pytest-fixtures-1b607b5c19ed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Understanding the structure of tox configuration file: &lt;a href="https://tox.wiki/en/latest/config.html"&gt;https://tox.wiki/en/latest/config.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Finally my &lt;code&gt;TDD In Python&lt;/code&gt; course: 
&lt;a href="https://www.youtube.com/watch?v=NAjCDS-qxrQ&amp;amp;list=PLVWk3kKbCAAuPPr_eh4KNKdL2hDSNFH3H"&gt;&lt;/a&gt;&lt;a href="https://www.youtube.com/watch?v=NAjCDSqxrQ&amp;amp;list=PLVWk3kKbCAAuPPr_eh4KNKdL2hDSNFH3H"&gt;https://www.youtube.com/watch?v=NAjCDSqxrQ&amp;amp;amp;list=PLVWk3kKbCAAuPPr_eh4KNKdL2hDSNFH3H&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tox</category>
      <category>testing</category>
      <category>pytest</category>
    </item>
  </channel>
</rss>
