<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Testing | Haobin Tan</title><link>https://haobin-tan.netlify.app/tags/testing/</link><atom:link href="https://haobin-tan.netlify.app/tags/testing/index.xml" rel="self" type="application/rss+xml"/><description>Testing</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Fri, 02 Dec 2022 00:00:00 +0000</lastBuildDate><image><url>https://haobin-tan.netlify.app/media/icon_hu7d15bc7db65c8eaf7a4f66f5447d0b42_15095_512x512_fill_lanczos_center_3.png</url><title>Testing</title><link>https://haobin-tan.netlify.app/tags/testing/</link></image><item><title>Testing</title><link>https://haobin-tan.netlify.app/docs/coding/python/testing/</link><pubDate>Fri, 02 Dec 2022 00:00:00 +0000</pubDate><guid>https://haobin-tan.netlify.app/docs/coding/python/testing/</guid><description/></item><item><title>Getting Started with Python Testing</title><link>https://haobin-tan.netlify.app/docs/coding/python/testing/py_test_getting_started/</link><pubDate>Fri, 02 Dec 2022 00:00:00 +0000</pubDate><guid>https://haobin-tan.netlify.app/docs/coding/python/testing/py_test_getting_started/</guid><description>&lt;h2 id="testing-your-code">Testing Your Code&lt;/h2>
&lt;h3 id="automated-vs-manual-testing">Automated vs. Manual Testing&lt;/h3>
&lt;p>&lt;strong>Exploratory testing&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>A form of testing that is done without a plan. You’re just exploring the application.&lt;/li>
&lt;li>To have a complete set of manual tests, all you need to do is make a list of all the features your application has, the different types of input it can accept, and the expected results.&lt;/li>
&lt;li>Every time you make a change to your code, you need to go through every single item on that list and check it. 🤯&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Automated testing&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Execution of your test plan (the parts of your application you want to test, the order in which you want to test them, and the expected responses) by a script instead of a human&lt;/li>
&lt;/ul>
&lt;h3 id="unit-tests-vs-integration-tests">Unit Tests vs. Integration Tests&lt;/h3>
&lt;p>&lt;strong>Integration testing&lt;/strong>: testing &lt;em>multiple&lt;/em> components&lt;/p>
&lt;ul>
&lt;li>Major challenge: When an integration test doesn’t give the right result, it’s very hard to diagnose the issue without being able to isolate which part of the system is failing.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Unit test&lt;/strong>: checks that a &lt;em>single&lt;/em> component operates in the right way → helps you to isolate what is broken in your application and fix it faster.&lt;/p>
&lt;h3 id="choosing-a-test-runner">Choosing a Test Runner&lt;/h3>
&lt;p>The three most popular test runners are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>unittest&lt;/code>&lt;/li>
&lt;li>&lt;code>nose&lt;/code> or &lt;code>nose2&lt;/code>&lt;/li>
&lt;li>&lt;code>pytest&lt;/code>&lt;/li>
&lt;/ul>
&lt;h4 id="unittest">&lt;code>unittest&lt;/code>&lt;/h4>
&lt;ul>
&lt;li>Contains both a testing framework and a test runner&lt;/li>
&lt;li>Important requirements for writing and executing tests
&lt;ul>
&lt;li>You put your tests into classes as methods&lt;/li>
&lt;li>You use a series of special assertion methods in the &lt;code>unittest.TestCase&lt;/code> class instead of the built-in &lt;code>assert&lt;/code> statement&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;code>nose&lt;/code>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Compatible with any tests written using the &lt;code>unittest&lt;/code> framework and can be used as a drop-in replacement for the &lt;code>unittest&lt;/code> test runner&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If you’re starting from scratch, it is recommended that you use &lt;code>nose2&lt;/code> instead of &lt;code>nose&lt;/code>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;code>pytest&lt;/code>&lt;/p>
&lt;ul>
&lt;li>Supports execution of &lt;code>unittest&lt;/code> test cases&lt;/li>
&lt;li>The real advantage of &lt;code>pytest&lt;/code> comes by writing &lt;code>pytest&lt;/code> test cases. ( &lt;code>pytest&lt;/code> test cases are a series of functions in a Python file starting with the name &lt;code>test_&lt;/code>.)&lt;/li>
&lt;li>Other great features
&lt;ul>
&lt;li>Support for the built-in &lt;code>assert&lt;/code> statement instead of using special &lt;code>self.assert*()&lt;/code> methods&lt;/li>
&lt;li>Support for filtering for test cases&lt;/li>
&lt;li>Ability to rerun from the last failing test&lt;/li>
&lt;li>An ecosystem of hundreds of plugins to extend the functionality&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Choosing the best test runner for your requirements and level of experience is important.&lt;/p>
&lt;p>Here we will use &lt;code>unittest&lt;/code>, the Python built-in standard library.&lt;/p>
&lt;h2 id="writing-your-first-test">Writing Your First Test&lt;/h2>
&lt;h3 id="where-to-write-the-test">Where to Write the Test&lt;/h3>
&lt;ul>
&lt;li>You can create a folder called &lt;code>tests/&lt;/code> and split the tests into multiple files
&lt;ul>
&lt;li>It is convention to ensure each file starts with &lt;code>test_&lt;/code> so all test runners will assume that Python file contains tests to be executed.&lt;/li>
&lt;li>Some very large projects split tests into more subdirectories based on their purpose or usage.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="how-to-structure-a-simple-test">How to Structure a Simple Test&lt;/h3>
&lt;p>Before you dive into writing tests, you’ll want to first make a couple of decisions:&lt;/p>
&lt;ol>
&lt;li>What do you want to test?&lt;/li>
&lt;li>Are you writing a unit test or an integration test?&lt;/li>
&lt;/ol>
&lt;p>Then the structure of a test should loosely follow this workflow:&lt;/p>
&lt;ol>
&lt;li>Create your inputs&lt;/li>
&lt;li>Execute the code being tested, capturing the output&lt;/li>
&lt;li>Compare the output with an expected result&lt;/li>
&lt;/ol>
&lt;h4 id="example">Example&lt;/h4>
&lt;p>Our project folder looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── my_sum/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── test.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In &lt;code>my_sum/__init__.py&lt;/code> there is a function called &lt;code>sum()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arg&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;takes an iterable (a list, tuple, or set) and adds the values together&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">total&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">val&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">arg&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">total&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">val&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">total&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The most simple test would be a list of integers. So, in &lt;code>test.py&lt;/code> with the following Python code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">unittest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">my_sum&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="nb">sum&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TestSum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_list_int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Test that it can sum a list of integers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Execute the test runner by discovering all classes in this file that inherit from `unittest.TestCase`.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-to-write-assertions">How to Write Assertions&lt;/h3>
&lt;p>&lt;strong>Assertion&lt;/strong>: validate the output against a known response.&lt;/p>
&lt;p>Some general best practices around how to write assertions:&lt;/p>
&lt;ul>
&lt;li>Make sure tests are repeatable and run your test multiple times to make sure it gives the same result every time&lt;/li>
&lt;li>Try and assert results that relate to your input data, such as checking that the result is the actual sum of values in the &lt;code>sum()&lt;/code> example&lt;/li>
&lt;/ul>
&lt;p>Some of the most commonly used methods in &lt;code>unittest&lt;/code>:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Equivalent to&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>.assertEqual(a, b)&lt;/code>&lt;/td>
&lt;td>&lt;code>a == b&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertTrue(x)&lt;/code>&lt;/td>
&lt;td>&lt;code>bool(x) is True&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertFalse(x)&lt;/code>&lt;/td>
&lt;td>&lt;code>bool(x) is False&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertIs(a, b)&lt;/code>&lt;/td>
&lt;td>&lt;code>a is b&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertIsNone(x)&lt;/code>&lt;/td>
&lt;td>&lt;code>x is None&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertIn(a, b)&lt;/code>&lt;/td>
&lt;td>&lt;code>a in b&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>.assertIsInstance(a, b)&lt;/code>&lt;/td>
&lt;td>&lt;code>isinstance(a, b)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>.assertIs()&lt;/code>, &lt;code>.assertIsNone()&lt;/code>, &lt;code>.assertIn()&lt;/code>, and &lt;code>.assertIsInstance()&lt;/code> all have opposite methods, named &lt;code>.assertIsNot()&lt;/code>, and so forth.&lt;/p>
&lt;h3 id="side-effects">Side Effects&lt;/h3>
&lt;p>&lt;strong>Side effects&lt;/strong> means executing a piece of code will alter other things in the environment, such as the attribute of a class, a file on the filesystem, or a value in a database.&lt;/p>
&lt;p>Side effects are quiet often and are an important part of testing. Decide if the side effect is being tested before including it in your list of assertions.&lt;/p>
&lt;p>If you find that the unit of code you want to test has lots of side effects, you might be breaking the &lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">Single Responsibility Principle&lt;/a>.&lt;/p>
&lt;ul>
&lt;li>Breaking the Single Responsibility Principle means the piece of code is doing &lt;em>too many&lt;/em> things and would be better off being refactored.&lt;/li>
&lt;li>Following the Single Responsibility Principle is a great way to design code that it is easy to write repeatable and simple unit tests for, and ultimately, reliable applications.&lt;/li>
&lt;/ul>
&lt;h3 id="executing-test-runners">Executing Test Runners&lt;/h3>
&lt;p>The Python application that executes your test code, checks the assertions, and gives you test results in your console is called the &lt;strong>test runner&lt;/strong>.&lt;/p>
&lt;p>There are many ways to execute the &lt;code>unittest&lt;/code> test runner:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>When you have a single test file named &lt;code>test.py&lt;/code>, simply run &lt;code>python test.py&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Another way is using the &lt;code>unittest&lt;/code> command line&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python -m unittest &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>
&lt;p>You can provide additional options to change the output. One of those is &lt;code>-v&lt;/code> for verbose&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python -m unittest -v &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">test_list_int &lt;span class="o">(&lt;/span>test.TestSum&lt;span class="o">)&lt;/span> ... ok
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Ran &lt;span class="m">1&lt;/span> tests in 0.000s
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Instead of providing the name of a module containing tests, you can request an auto-discovery&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python -m unittest discover
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will search the current directory for any files named &lt;code>test*.py&lt;/code> and attempt to test them.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Once you have multiple test files, as long as you follow the &lt;code>test*.py&lt;/code> naming pattern, you can provide the name of the directory instead by using the &lt;code>-s&lt;/code> flag and the name of the directory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python -m unittest discover -s tests
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>unittest&lt;/code> will run all tests in a single test plan and give you the results.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If your source code is not in the directory root and contained in a subdirectory, for example in a folder called &lt;code>src/&lt;/code>, you can tell &lt;code>unittest&lt;/code> where to execute the tests so that it can import the modules correctly with the &lt;code>-t&lt;/code> flag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python -m unittest discover -s tests -t src
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>unittest&lt;/code> will change to the &lt;code>src/&lt;/code> directory, scan for all &lt;code>test*.py&lt;/code> files inside the the &lt;code>tests&lt;/code> directory, and execute them.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="more-advanced-testing-scenarios">More Advanced Testing Scenarios&lt;/h2>
&lt;p>Remember the three basic steps of every test:&lt;/p>
&lt;ol>
&lt;li>Create your inputs&lt;/li>
&lt;li>Execute the code, capturing the output&lt;/li>
&lt;li>Compare the output with an expected result&lt;/li>
&lt;/ol>
&lt;p>However, it’s not always as easy as creating a static value for the input like a string or a number. Sometimes, your application will require an instance of a class or a context.&lt;/p>
&lt;p>The data that you create as an input is known as a &lt;strong>fixture&lt;/strong>. It’s common practice to create fixtures and reuse them.&lt;/p>
&lt;p>If you’re running the same test and passing different values each time and expecting the same result, this is known as &lt;strong>parameterization&lt;/strong>.&lt;/p>
&lt;h3 id="handling-expected-failures">Handling Expected Failures&lt;/h3>
&lt;p>To test an expected error without causing the test to fail, use &lt;code>.assertRaises()&lt;/code> as a context-manager, then inside the &lt;code>with&lt;/code> block execute the test steps.&lt;/p>
&lt;p>E.g. we test the &lt;code>sum()&lt;/code> method, providing it with a bad value, such as a single integer or a string.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">unittest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">my_sum&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="nb">sum&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TestSum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_list_int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Test that it can sum a list of integers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_bad_type&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> This test case will now only pass if sum(data) raises a TypeError.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> You can replace TypeError with any exception type you choose.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;banana&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertRaises&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="ne">TypeError&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="isolating-behaviors-in-your-application">Isolating Behaviors in Your Application&lt;/h3>
&lt;p>Some simple techniques you can use to test parts of your application that have many side effects:&lt;/p>
&lt;ul>
&lt;li>Refactoring code to follow the Single Responsibility Principle&lt;/li>
&lt;li>Mocking out any method or function calls to remove side effects&lt;/li>
&lt;li>Using integration testing instead of unit testing for this piece of the application&lt;/li>
&lt;/ul>
&lt;h3 id="writing-integration-tests">Writing Integration Tests&lt;/h3>
&lt;p>Integration testing is the testing of multiple components of the application to check that they work together. Integration testing might require acting like a consumer or user of the application by:&lt;/p>
&lt;ul>
&lt;li>Calling an HTTP REST API&lt;/li>
&lt;li>Calling a Python API&lt;/li>
&lt;li>Calling a web service&lt;/li>
&lt;li>Running a command line&lt;/li>
&lt;/ul>
&lt;p>Each of these types of integration tests can be written in the same way as a unit test, following the Input, Execute, and Assert pattern.&lt;/p>
&lt;p>Significant difference from unit tests:&lt;/p>
&lt;ul>
&lt;li>Integration tests are checking more components at once and therefore will have more side effects than a unit test&lt;/li>
&lt;li>Integration tests will require more fixtures to be in place, like a database, a network socket, or a configuration file.&lt;/li>
&lt;/ul>
&lt;p>A good practice is to &lt;strong>separate your unit tests and your integration tests&lt;/strong>. A simple way to separate unit and integration tests is simply to put them in different folders:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── my_app/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── unit/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | └── test_sum.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── integration/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test_integration.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="automating-test-execution">Automating Test Execution&lt;/h2>
&lt;p>There are some tools for executing tests &lt;em>automatically&lt;/em> when you make changes and commit them to a source-control repository like Git. Automated testing tools are often known as &lt;strong>CI/CD&lt;/strong> tools, which stands for “Continuous Integration/Continuous Deployment.” They can run your tests, compile and publish any applications, and even deploy them into production.&lt;/p>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://realpython.com/python-testing/#automating-the-execution-of-your-tests">Getting Started With Testing in Python&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Effective Python Testing With Pytest</title><link>https://haobin-tan.netlify.app/docs/coding/python/testing/pytest/</link><pubDate>Fri, 02 Dec 2022 00:00:00 +0000</pubDate><guid>https://haobin-tan.netlify.app/docs/coding/python/testing/pytest/</guid><description>&lt;h2 id="what-makes-pytest-so-useful">What Makes &lt;code>pytest&lt;/code> So Useful?&lt;/h2>
&lt;p>With &lt;code>pytest&lt;/code>, common tasks require less code and advanced tasks can be achieved through a variety of time-saving commands and plugins. It’ll even run your existing tests out of the box, including those written with &lt;code>unittest&lt;/code>.&lt;/p>
&lt;h3 id="less-boilerplate">Less Boilerplate&lt;/h3>
&lt;p>Most functional tests follow the Arrange-Act-Assert model:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Arrange&lt;/strong>, or set up, the conditions for the test&lt;/li>
&lt;li>&lt;strong>Act&lt;/strong> by calling some function or method&lt;/li>
&lt;li>&lt;strong>Assert&lt;/strong> that some end condition is true&lt;/li>
&lt;/ol>
&lt;p>&lt;code>pytest&lt;/code> simplifies this workflow by allowing you to use normal functions and Python’s &lt;code>assert&lt;/code> keyword directl. For example&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># test_with_pytest.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_always_passes&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_always_fails&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Advantages of &lt;code>pytest&lt;/code> against the Python built-in &lt;code>unittest&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>You don’t have to deal with any imports or classes. All you need to do is include a function with the &lt;code>test_&lt;/code> prefix.&lt;/li>
&lt;li>You can use the &lt;code>assert&lt;/code> keyword, you don’t need to learn or remember all the different &lt;code>self.assert*&lt;/code> methods in &lt;code>unittest&lt;/code>&lt;/li>
&lt;li>&lt;code>pytest&lt;/code> provides you with a much more detailed and easy-to-read output.&lt;/li>
&lt;/ul>
&lt;h3 id="nicer-output">Nicer Output&lt;/h3>
&lt;p>Run test suite using the &lt;code>pytest&lt;/code> command from the top-level folder of the project:&lt;/p>
&lt;p>&lt;img src="https://raw.githubusercontent.com/EckoTan0804/upic-repo/master/uPic/%E6%88%AA%E5%B1%8F2022-12-02%2021.32.33.png" alt="截屏2022-12-02 21.32.33">&lt;/p>
&lt;p>The report shows:&lt;/p>
&lt;ol>
&lt;li>The system state, including which versions of Python, &lt;code>pytest&lt;/code>, and any plugins you have installed&lt;/li>
&lt;li>The &lt;code>rootdir&lt;/code>, or the directory to search under for configuration and tests&lt;/li>
&lt;li>The number of tests the runner discovered&lt;/li>
&lt;/ol>
&lt;p>The output indicates the status of each test using a syntax similar to &lt;code>unittest&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>A dot (&lt;code>.&lt;/code>)&lt;/strong> means that the test passed.&lt;/li>
&lt;li>&lt;strong>An &lt;code>F&lt;/code>&lt;/strong> means that the test has failed.&lt;/li>
&lt;li>&lt;strong>An &lt;code>E&lt;/code>&lt;/strong> means that the test raised an unexpected exception.&lt;/li>
&lt;/ul>
&lt;p>For tests that fail, the report gives a detailed breakdown of the failure. This extra output can come in extremely handy when debugging.&lt;/p>
&lt;h3 id="less-to-learn">Less to Learn&lt;/h3>
&lt;p>Being able to use the &lt;a href="https://realpython.com/python-assert-statement/">&lt;code>assert&lt;/code>&lt;/a> keyword is powerful, which means that there’s nothing new to learn. Writing test with &lt;code>pytest&lt;/code> looks very much like normal Python functions. All of this makes the learning curve for &lt;code>pytest&lt;/code> shallower than it is for &lt;code>unittest&lt;/code>.&lt;/p>
&lt;h3 id="easier-to-manage-state-and-dependencies">Easier to Manage State and Dependencies&lt;/h3>
&lt;p>Your tests will often depend on types of data or &lt;a href="https://en.wikipedia.org/wiki/Test_double">test doubles&lt;/a> that mock objects your code is likely to encounter, such as &lt;a href="https://realpython.com/python-dicts/">dictionaries&lt;/a> or &lt;a href="https://realpython.com/python-json/">JSON&lt;/a> files.&lt;/p>
&lt;p>With &lt;code>unittest&lt;/code>, you might extract these dependencies into &lt;code>.setUp()&lt;/code> and &lt;code>.tearDown()&lt;/code> methods so that each test in the class can make use of them. However, as your test classes get larger, you may inadvertently make the test’s dependence entirely &lt;strong>implicit&lt;/strong>. In other words, by looking at one of the many tests in isolation, you may not immediately see that it depends on something else.&lt;/p>
&lt;p>&lt;code>pytest&lt;/code> leads you toward &lt;strong>explicit&lt;/strong> dependency declarations that are still reusable thanks to the availability of &lt;a href="https://docs.pytest.org/en/latest/fixture.html">&lt;strong>fixtures&lt;/strong>&lt;/a>.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>pytest&lt;/code> fixtures are functions that can create data, test doubles, or initialize system state for the test suite.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Any test that wants to use a fixture must explicitly use this fixture function as an argument to the test function, so dependencies are always stated up front&lt;/p>
&lt;p>Example&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># fixture_demo.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.fixture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">example_fixture&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_with_fixture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">example_fixture&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># You can immediately tell that it depends on a fixture, &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># without needing to check the whole file for fixture definitions.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">example_fixture&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Fixtures can also make use of other fixtures, again by declaring them explicitly as dependencies.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="easy-to-filter-tests">Easy to Filter Tests&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Name-based filtering&lt;/strong>: You can limit &lt;code>pytest&lt;/code> to running only those tests whose fully qualified names match a particular expression. You can do this with the &lt;code>-k&lt;/code> parameter.&lt;/li>
&lt;li>&lt;strong>Directory scoping&lt;/strong>: By default, &lt;code>pytest&lt;/code> will run only those tests that are in or under the current directory.&lt;/li>
&lt;li>&lt;strong>Test categorization&lt;/strong>: &lt;code>pytest&lt;/code> can include or exclude tests from particular categories that you define. You can do this with the &lt;code>-m&lt;/code> parameter.&lt;/li>
&lt;/ul>
&lt;h3 id="allows-test-parametrization">Allows Test Parametrization&lt;/h3>
&lt;p>&lt;code>pytest&lt;/code> offers its own solution in which each test can pass or fail independently. More see: &lt;a href="#parametrization-combining-tests">Parametrization: Combining Tests&lt;/a>.&lt;/p>
&lt;h3 id="plugin-based-architecture">Plugin-Based Architecture&lt;/h3>
&lt;p>One of the most beautiful features of &lt;code>pytest&lt;/code> is its openness to customization and new features. Almost every piece of the program can be cracked open and changed. As a result, &lt;code>pytest&lt;/code> users have developed a rich ecosystem of helpful plugins.&lt;/p>
&lt;h2 id="fixtures-managing-state-and-dependencies">Fixtures: Managing State and Dependencies&lt;/h2>
&lt;ul>
&lt;li>&lt;code>pytest&lt;/code> fixtures are a way of providing data, test doubles, or state setup to your tests.&lt;/li>
&lt;li>Fixtures are functions that can return a wide range of values.&lt;/li>
&lt;li>Each test that depends on a fixture must explicitly accept that fixture as an argument.&lt;/li>
&lt;/ul>
&lt;h3 id="when-to-create-fixtures">When to Create Fixtures&lt;/h3>
&lt;p>If you find yourself writing several tests that all make use of the &lt;em>same&lt;/em> underlying test data, then a fixture may be in your future. &lt;strong>You can pull the repeated data into a single function decorated with &lt;code>@pytest.fixture&lt;/code> to indicate that the function is a &lt;code>pytest&lt;/code> fixture.&lt;/strong>&lt;/p>
&lt;h4 id="example">Example&lt;/h4>
&lt;p>Let&amp;rsquo;s say we write functions to process the data returned by an API endpoint. The data represents a list of people, each with a given name, family name, and job title. One function &lt;code>format_data_for_display()&lt;/code> should output a list of strings that include each person’s full name (their &lt;code>given_name&lt;/code> followed by their &lt;code>family_name&lt;/code>), a colon, and their &lt;code>title&lt;/code>. Another function &lt;code>format_data_for_excel()&lt;/code> should transform the data into comma-separated values for use in Excel.&lt;/p>
&lt;p>Without using fixtures, the test script looks like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># test_format_data.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_format_data_for_display&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">people&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alfonsa&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Ruiz&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Senior Software Engineer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Sayid&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Khan&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Project Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">format_data_for_display&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">people&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Alfonsa Ruiz: Senior Software Engineer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Sayid Khan: Project Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_format_data_for_excel&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">people&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alfonsa&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Ruiz&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Senior Software Engineer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Sayid&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Khan&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Project Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">format_data_for_excel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">people&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;given,family,title
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Alfonsa,Ruiz,Senior Software Engineer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Sayid,Khan,Project Manager
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notably, both the tests have to repeat the definition of the &lt;code>people&lt;/code> variable, which is quite a few lines of code.&lt;/p>
&lt;p>With fixture:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># test_format_data.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.fixture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">example_people_data&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alfonsa&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Ruiz&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Senior Software Engineer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;given_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Sayid&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;family_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Khan&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Project Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_format_data_for_display&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">example_people_data&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">format_data_for_display&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">example_people_data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Alfonsa Ruiz: Senior Software Engineer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Sayid Khan: Project Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_format_data_for_excel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">example_people_data&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">format_data_for_excel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">example_people_data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;given,family,title
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Alfonsa,Ruiz,Senior Software Engineer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Sayid,Khan,Project Manager
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each test is now notably shorter but still has a clear path back to the data it depends on 👏. Be sure to name your fixture something specific. That way, you can quickly determine if you want to use it when writing new tests in the future!&lt;/p>
&lt;h3 id="when-to-avoid-fixtures">When to Avoid Fixtures&lt;/h3>
&lt;p>Fixtures are great for extracting data or objects that you use across multiple tests. However, they aren’t always as good for tests that require slight variations in the data. Littering your test suite with fixtures is no better than littering it with plain data or objects. It might even be worse because of the added layer of indirection.&lt;/p>
&lt;p>As with most abstractions, it takes some practice and thought to find the right level of fixture use.&lt;/p>
&lt;p>Nevertheless, fixtures will likely be an integral part of your test suite. As your project grows in scope, the challenge of scale starts to come into the picture. One of the challenges facing any kind of tool is how it handles being used at scale, and luckily, &lt;code>pytest&lt;/code> has a bunch of useful features that can help you manage the complexity that comes with growth.&lt;/p>
&lt;h3 id="how-to-use-fixtures-at-scale">How to Use Fixtures at Scale&lt;/h3>
&lt;p>In &lt;code>pytest&lt;/code>, fixtures are &lt;strong>modular&lt;/strong>. Being modular means that fixtures can be &lt;a href="https://realpython.com/python-import/">imported&lt;/a>, can import other modules, and they can depend on and import other fixtures. → All this allows you to compose a suitable fixture abstraction for your use case.&lt;/p>
&lt;p>For example, you may find that fixtures in two separate files, or &lt;a href="https://realpython.com/python-modules-packages/">modules&lt;/a>, share a common dependency. In this case, you can move fixtures from test modules into more general fixture-related modules. That way, you can import them back into any test modules that need them. This is a good approach when you find yourself using a fixture repeatedly throughout your project.&lt;/p>
&lt;p>If you want to make a fixture available for your whole project without having to import it, a special configuration module called &lt;a href="https://docs.pytest.org/en/6.2.x/fixture.html#conftest-py-sharing-fixtures-across-multiple-files">&lt;code>conftest.py&lt;/code>&lt;/a> will allow you to do that.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pytest&lt;/code> looks for a &lt;code>conftest.py&lt;/code> module in each directory.&lt;/li>
&lt;li>If you add your general-purpose fixtures to the &lt;code>conftest.py&lt;/code> module, then you’ll be able to use that fixture throughout the module’s parent directory and in any subdirectories without having to import it. → This is a great place to put your most widely used fixtures.&lt;/li>
&lt;/ul>
&lt;p>Another interesting use case for fixtures and &lt;code>conftest.py&lt;/code> is in guarding access to resources (&lt;em>e.g.&lt;/em> test suite for code that deal with API calls). &lt;code>pytest&lt;/code> provides a &lt;a href="https://docs.pytest.org/en/latest/monkeypatch.html">&lt;code>monkeypatch&lt;/code>&lt;/a> fixture to replace values and behaviors, which you can use to great effect. E.g.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># conftest.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">requests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.fixture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">autouse&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">disable_network_calls&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">monkeypatch&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">stunted_get&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">RuntimeError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Network access not allowed during testing!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">monkeypatch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">setattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">requests&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;get&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">stunted_get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="marks-categorizing-tests">Marks: Categorizing Tests&lt;/h2>
&lt;p>In any large test suite, it would be nice to avoid running &lt;em>all&lt;/em> the tests when you’re trying to iterate quickly on a new feature. To not run all tests, you can take advantage of &lt;strong>markers&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>You can define categories for your tests and provides options for including or excluding categories when you run your suite.&lt;/li>
&lt;li>You can mark a test with any number of categories.&lt;/li>
&lt;/ul>
&lt;p>Marking tests is useful for categorizing tests by subsystem or dependencies.&lt;/p>
&lt;div class="flex px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
&lt;span class="pr-3 pt-1 text-primary-600 dark:text-primary-300">
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m11.25 11.25l.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0m-9-3.75h.008v.008H12z"/>&lt;/svg>
&lt;/span>
&lt;span class="dark:text-neutral-300">&lt;p>Sometimes it can be easy to mistype or misremember the name of a mark. &lt;code>pytest&lt;/code> will warn you about marks that it doesn’t recognize in the test output.&lt;/p>
&lt;p>You can use the &lt;code>--strict-markers&lt;/code> flag to the &lt;code>pytest&lt;/code> command to ensure that all marks in your tests are registered in your &lt;code>pytest&lt;/code> configuration file, &lt;code>pytest.ini&lt;/code>. It’ll prevent you from running your tests until you register any unknown marks.&lt;/p>
&lt;p>More see: &lt;a href="https://docs.pytest.org/en/latest/mark.html#registering-marks">&lt;code>pytest&lt;/code> documentation&lt;/a>.&lt;/p>
&lt;/span>
&lt;/div>
&lt;p>&lt;code>ytest&lt;/code> provides a few marks out of the box:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>skip&lt;/code>&lt;/strong> skips a test unconditionally.&lt;/li>
&lt;li>&lt;strong>&lt;code>skipif&lt;/code>&lt;/strong> skips a test if the expression passed to it evaluates to &lt;code>True&lt;/code>.&lt;/li>
&lt;li>&lt;strong>&lt;code>xfail&lt;/code>&lt;/strong> indicates that a test is expected to fail, so if the test &lt;em>does&lt;/em> fail, the overall suite can still result in a passing status.&lt;/li>
&lt;li>&lt;strong>&lt;code>parametrize&lt;/code>&lt;/strong> creates multiple variants of a test with different values as arguments. You’ll learn more about this mark shortly.&lt;/li>
&lt;/ul>
&lt;p>You can see a list of all the marks that &lt;code>pytest&lt;/code> knows about by running &lt;code>pytest --markers&lt;/code>.&lt;/p>
&lt;h2 id="parametrization-combining-tests">Parametrization: Combining Tests&lt;/h2>
&lt;p>We have seen that fixtures can be used to reduce code duplication by extracting common dependencies. Nevertheless, fixtures aren’t quite as useful when you have several tests with slightly different inputs and expected outputs.&lt;/p>
&lt;p>In these cases, you can &lt;a href="http://doc.pytest.org/en/latest/example/parametrize.html">&lt;strong>parametrize&lt;/strong>&lt;/a> a single test definition, and &lt;code>pytest&lt;/code> will create variants of the test for you with the parameters you specify.&lt;/p>
&lt;p>Let&amp;rsquo;s say you’ve written a function to tell if a string is a &lt;a href="https://en.wikipedia.org/wiki/Palindrome">palindrome&lt;/a>. An initial set of tests could look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_empty_string&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_single_character&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_mixed_casing&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Bob&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_with_spaces&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Never odd or even&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_with_punctuation&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Do geese see God?&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_not_palindrome&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;abc&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_not_quite&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;abab&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All of these tests except the last two have the same shape:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="ow">in&lt;/span> &lt;span class="n">some&lt;/span> &lt;span class="n">situation&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;some string&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To get rid of the boilerplate, you can use &lt;code>@pytest.mark.parametrize()&lt;/code> to fill in this shape with different values, reducing your test code significantly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;palindrome&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Bob&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Never odd or even&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Do geese see God?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">palindrome&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">palindrome&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;non_palindrome&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;abc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;abab&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome_not_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">non_palindrome&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">non_palindrome&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>The first argument to &lt;code>parametrize()&lt;/code> is a comma-delimited string of parameter names. You don’t have to provide more than one name&lt;/li>
&lt;li>The second argument is a &lt;a href="https://realpython.com/python-lists-tuples/#python-lists">list&lt;/a> of either &lt;a href="https://realpython.com/python-lists-tuples/#python-tuples">tuples&lt;/a> or single values that represent the parameter value(s).&lt;/li>
&lt;/ul>
&lt;p>You could take your parametrization a step further to combine all your tests into one:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;maybe_palindrome, expected_result&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Bob&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Never odd or even&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Do geese see God?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;abc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">False&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;abab&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">False&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">maybe_palindrome&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">expected_result&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">is_palindrome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">maybe_palindrome&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">expected_result&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Keep in mind that &lt;strong>make sure you’re not parametrizing your test suite into incomprehensibility.&lt;/strong>&lt;/p>
&lt;p>You can use parametrization to separate the test data from the test behavior so that it’s clear what the test is testing, and also to make the different test cases easier to read and maintain.&lt;/p>
&lt;h2 id="durations-reports-fighting-slow-tests">Durations Reports: Fighting Slow Tests&lt;/h2>
&lt;p>If you want to improve the speed of your tests, then it’s useful to know &lt;em>which&lt;/em> tests might offer the biggest improvements. &lt;code>pytest&lt;/code> can automatically record test durations for you and report the top offenders.&lt;/p>
&lt;p>Use the &lt;code>--durations&lt;/code> option to the &lt;code>pytest&lt;/code> command to include a duration report in your test results.&lt;/p>
&lt;ul>
&lt;li>&lt;code>--durations&lt;/code> expects an integer value &lt;code>n&lt;/code> and will report the slowest &lt;code>n&lt;/code> number of tests.&lt;/li>
&lt;/ul>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">(&lt;/span>venv&lt;span class="o">)&lt;/span> $ pytest --durations&lt;span class="o">=&lt;/span>&lt;span class="m">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">=============================&lt;/span> slowest &lt;span class="m">5&lt;/span> &lt;span class="nv">durations&lt;/span> &lt;span class="o">=============================&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3.03s call test_code.py::test_request_read_timeout
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1.07s call test_code.py::test_request_connection_timeout
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0.57s call test_code.py::test_database_read
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">(&lt;/span>&lt;span class="m">2&lt;/span> durations &amp;lt; 0.005s hidden. Use -vv to show these durations.&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">===========================&lt;/span> short &lt;span class="nb">test&lt;/span> summary &lt;span class="nv">info&lt;/span> &lt;span class="o">===========================&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Short durations are hidden by default&lt;/li>
&lt;li>Each test that shows up in the durations report is a good candidate to speed up because it takes an above-average amount of the total testing time.&lt;/li>
&lt;/ul>
&lt;h2 id="useful-pytest-plugins">Useful &lt;code>pytest&lt;/code> Plugins&lt;/h2>
&lt;p>You can see which other plugins are available for &lt;code>pytest&lt;/code> with this extensive &lt;a href="http://plugincompat.herokuapp.com/">list of third-party plugins&lt;/a>.&lt;/p>
&lt;h3 id="pytest-randomly">&lt;code>pytest-randomly&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/pytest-dev/pytest-randomly">&lt;code>pytest-randomly&lt;/code>&lt;/a> forces your tests to run in a random order. &lt;code>pytest&lt;/code> always collects all the tests it can find before running them. &lt;code>pytest-randomly&lt;/code> just shuffles that list of tests before execution.&lt;/p>
&lt;p>The plugin will print a seed value in the configuration description. You can use that value to run the tests in the same order as you try to fix the issue.&lt;/p>
&lt;h3 id="pytest-cov">&lt;code>pytest-cov&lt;/code>&lt;/h3>
&lt;p>If you want to measure how well your tests cover your implementation code, then you can use the &lt;a href="https://coverage.readthedocs.io/">coverage&lt;/a> package. &lt;a href="https://pytest-cov.readthedocs.io/en/latest/">&lt;code>pytest-cov&lt;/code>&lt;/a> integrates coverage, so you can run &lt;code>pytest --cov&lt;/code> to see the test coverage report and boast about it on your project front page.&lt;/p>
&lt;h3 id="pytest-django">&lt;code>pytest-django&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://pytest-django.readthedocs.io/en/latest/">&lt;code>pytest-django&lt;/code>&lt;/a> provides a handful of useful fixtures and marks for dealing with Django tests.&lt;/p>
&lt;h3 id="pytest-bdd">&lt;code>pytest-bdd&lt;/code>&lt;/h3>
&lt;p>&lt;code>pytest&lt;/code> can be used to run tests that fall outside the traditional scope of unit testing. &lt;a href="https://en.wikipedia.org/wiki/Behavior-driven_development">Behavior-driven development&lt;/a> (BDD) encourages writing plain-language descriptions of likely user actions and expectations, which you can then use to determine whether to implement a given feature.&lt;/p>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://realpython.com/pytest-python-testing/#parametrization-combining-tests">Effective Python Testing With Pytest&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>