{"id":338,"date":"2025-01-24T10:00:00","date_gmt":"2025-01-24T16:00:00","guid":{"rendered":"https:\/\/douglasstarnes.dev\/?p=338"},"modified":"2025-01-23T22:30:08","modified_gmt":"2025-01-24T04:30:08","slug":"testing-python-applications-with-pytest-and-visual-studio-code","status":"publish","type":"post","link":"https:\/\/douglasstarnes.dev\/index.php\/2025\/01\/24\/testing-python-applications-with-pytest-and-visual-studio-code\/","title":{"rendered":"Testing Python Applications with pytest and Visual Studio Code"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">We try hard to write code that is free of bugs. But it&#8217;s impossible to avoid all bugs and errors in software. So we need to check our work and validate the design t and implementation of our software. For Python applications, we can use <code>pytest<\/code> to write tests that ensure our code is working as expected. This post will show how to write a simple class, including tests with <code>pytest<\/code>, and the features of Visual Studio Code intended to make running and managing tests easier.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Configuring the Development Environment<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The only thing you&#8217;ll need to run and manage Python tests in Visual Studio Code is the Python extension from Microsoft. The Python extension includes a test runner UI that lets you discover tests and run specific tests or groups of tests. It works with both the <code>unittest<\/code> model from the Python Standard Library as well as the <code>pytest<\/code> package I&#8217;ll use in this post. And I&#8217;ve also installed Ruff and configured it as the default formatter. I&#8217;ve done this in other posts so I won&#8217;t repeat the steps to do that here. Also keep in mind that everyone described in this post for Visual Studio Code will work almost identically in a GitHub Codespace. In fact, I&#8217;ll be using a GitHub Codespace for the screenshots in this post.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Class to Test<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here is the file structure of the demo project:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/src\n  \/app\n    __init__.py\n    bank_account.py\n  \/test\n    __init__.py\n    test_bank_account.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>__init__<\/code>.py files should be blank.  We&#8217;ll see the code for <code>\/src\/app\/bank_account.py<\/code> next.  And then later the tests will go in <code>\/src\/test\/test_bank_account.py<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The class that we will be writing tests against is a simple bank account.   The following code should be in <code>\/src\/app\/bank_account.py<\/code><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"import datetime\nfrom dataclasses import dataclass\nfrom enum import Enum\n\n\nclass BankAccountTransactionType(Enum):\n    DEPOSIT = 0\n    WITHDRAWAL = 1\n\n\n@dataclass\nclass BankAccountTransaction:\n    transaction_type: BankAccountTransactionType\n    amount: float\n    date: datetime.date\n\n\nclass BankAccount:\n    def __init__(self, owner: str, balance: float = 0.0):\n        if balance &lt; 0:\n            raise ValueError(&quot;Initial balance cannot be negative&quot;)\n        self.balance = balance\n        self.owner = owner\n        self.transactions = []\n\n    def withdraw(self, amount: float) -&gt; float:\n        if amount &lt; 0:\n            raise ValueError(&quot;Cannot withdraw negative amount&quot;)\n\n        if amount &gt; self.balance:\n            raise ValueError(&quot;Cannot withdraw more than balance&quot;)\n\n        self.balance -= amount\n        self.transactions.append(\n            BankAccountTransaction(\n                transaction_type=BankAccountTransactionType.WITHDRAWAL,\n                amount=amount,\n                date=datetime.date.today(),\n            )\n        )\n        return self.balance\n\n    def deposit(self, amount: float) -&gt; float:\n        if amount &lt; 0:\n            raise ValueError(&quot;Cannot deposit negative amount&quot;)\n        self.balance += amount\n        self.transactions.append(\n            BankAccountTransaction(\n                transaction_type=BankAccountTransactionType.DEPOSIT,\n                amount=amount,\n                date=datetime.date.today(),\n            )\n        )\n        return self.balance\n\n    def recent_transactions(self, n: int = 3) -&gt; list[BankAccountTransaction]:\n        if n &lt; len(self.transactions):\n            n = len(self.transactions)\n        recent = self.transactions[-n:]\n        recent.reverse()\n        return recent\n\n    def most_recent_transaction(self) -&gt; BankAccountTransaction:\n        return self.transactions[-1]\n\n    def check_balance(self) -&gt; float:\n        return self.balance\n\n    def __str__(self) -&gt; str:\n        return f&quot;&lt;BankAccount owner-{self.owner} balance-{self.balance}&gt;&quot;\n\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> datetime<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">from<\/span><span style=\"color: #D8DEE9FF\"> dataclasses <\/span><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> dataclass<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">from<\/span><span style=\"color: #D8DEE9FF\"> enum <\/span><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> Enum<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">BankAccountTransactionType<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #8FBCBB; font-weight: bold\">Enum<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    DEPOSIT <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    WITHDRAWAL <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">@<\/span><span style=\"color: #D08770\">dataclass<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">BankAccountTransaction<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    transaction_type<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> BankAccountTransactionType<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    amount<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    date<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> datetime<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">date<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">BankAccount<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">__init__<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">owner<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">str<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">balance<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.0<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> balance <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">raise<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">ValueError<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Initial balance cannot be negative<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> balance<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">owner <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> owner<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">withdraw<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">amount<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> amount <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">raise<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">ValueError<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Cannot withdraw negative amount<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> amount <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">raise<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">ValueError<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Cannot withdraw more than balance<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance <\/span><span style=\"color: #81A1C1\">-=<\/span><span style=\"color: #D8DEE9FF\"> amount<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">append<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #88C0D0\">BankAccountTransaction<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">transaction_type<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">BankAccountTransactionType<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">WITHDRAWAL<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">amount<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">amount<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">date<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">datetime<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">date<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">today<\/span><span style=\"color: #ECEFF4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">deposit<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">amount<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> amount <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">raise<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">ValueError<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Cannot deposit negative amount<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance <\/span><span style=\"color: #81A1C1\">+=<\/span><span style=\"color: #D8DEE9FF\"> amount<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">append<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #88C0D0\">BankAccountTransaction<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">transaction_type<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">BankAccountTransactionType<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">DEPOSIT<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">amount<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">amount<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">date<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">datetime<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">date<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">today<\/span><span style=\"color: #ECEFF4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">recent_transactions<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">n<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">[<\/span><span style=\"color: #D8DEE9FF\">BankAccountTransaction<\/span><span style=\"color: #ECEFF4\">]:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> n <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            n <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        recent <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">[<\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\">n<\/span><span style=\"color: #ECEFF4\">:]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        recent<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">reverse<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> recent<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">most_recent_transaction<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> BankAccountTransaction<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transactions<\/span><span style=\"color: #ECEFF4\">[<\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #ECEFF4\">]<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">check_balance<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">__str__<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">str<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&quot;&lt;BankAccount owner-<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">owner<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\"> balance-<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #81A1C1\">self<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">balance<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">&gt;&quot;<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">There isn&#8217;t a whole lot to this class.  The <code>BankAccount<\/code> class holds an <code>owner<\/code>, <code>balance<\/code>, and list of <code>transactions<\/code>.  The <code>BankAccountTransaction<\/code> class has a transaction type, the amount of the transaction and the date of the transaction.  Then you can deposit and withdraw funds and check the balance and most recent transactions.  Notice you cannot deposit and withdraw negative amounts. And you cannot withdraw more than the account balance.  Here is a sample usage:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"bank_account = BankAccount(&quot;New Owner&quot;)\nbank_account.deposit(1000.00)\nbank_account.withdraw(200.00)\nprint(f&quot;Current balance: ${bank_account.check_balance()}&quot;)\nprint(&quot;Recent transactions:&quot;)\nfor t in bank_account.recent_transactions():\n    print(\n        f&quot;{t.date.strftime('%Y\/%m\/%d')} | {t.transaction_type.name} | ${t.amount}&quot;\n    )\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">bank_account <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">BankAccount<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">New Owner<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">deposit<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">1000.00<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">withdraw<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">200.00<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">print<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&quot;Current balance: $<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #D8DEE9FF\">bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">check_balance<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">print<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Recent transactions:<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">for<\/span><span style=\"color: #D8DEE9FF\"> t <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">recent_transactions<\/span><span style=\"color: #ECEFF4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">print<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&quot;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #D8DEE9FF\">t<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">date<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">strftime<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">%Y\/%m\/<\/span><span style=\"color: #EBCB8B\">%d<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\"> | <\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #D8DEE9FF\">t<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">transaction_type<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">name<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\"> | $<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #D8DEE9FF\">t<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">amount<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Writing the First Test<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Again, for this post, we will be using the Python package <code>pytest<\/code>.  Unlike <code>unittest<\/code>, another Python test frameworks, <code>pytest<\/code> is not distributed with the Python Standard Library and thus must be installed with <code>pip<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ pip install pytest\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pip<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">install<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pytest<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Inside of <code>\/src\/test\/test_bank_account.py<\/code> import the <code>pytest<\/code> module and the <code>BankAccount<\/code> class.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"import pytest\n\nfrom app.bank_account import BankAccount\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">import<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pytest<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">from<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">app.bank_account<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">import<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">BankAccount<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now write a function that creates a new <code>BankAccount<\/code> and checks to ensure that the starting balance is zero:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"def test_bank_account_default_balance_is_zero():\n  bank_account = BankAccount(&quot;Account Owner&quot;)\n  assert bank_account.check_balance() == 0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">test_bank_account_default_balance_is_zero<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #A3BE8C\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #88C0D0\">bank_account<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">BankAccount<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #88C0D0\">&quot;Account Owner&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #88C0D0\">assert<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">bank_account.check_balance<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>assert<\/code> statement verifies that a condition is <code>True<\/code>.  If the <code>check_balance<\/code> function returns 0 the assertion will pass.  Otherwise it will fail.  To see if the test passes, run <code>pytest<\/code> from the <code>\/src<\/code> folder.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ cd src\n$ pytest\n============================= test session starts =============================\nplatform linux -- Python 3.12.1, pytest-8.3.4, pluggy-1.5.0\nrootdir: \/workspaces\/codespaces-blank\/src\nplugins: anyio-4.7.0\ncollected 1 item                                                                                                    \n\ntests\/test_bank_account.py .                                                                                  [100%]\n\n============================== 1 passed in 0.01s ==============================\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">cd<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">src<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pytest<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">============================= <\/span><span style=\"color: #A3BE8C\">test<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">session<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">starts<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">=============================<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">platform<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">linux<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">--<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">Python<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3.12<\/span><span style=\"color: #A3BE8C\">.1,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pytest-8.3.4,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pluggy-1.5.0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">rootdir:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/workspaces\/codespaces-blank\/src<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">plugins:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">anyio-4.7.0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">collected<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">item<\/span><span style=\"color: #D8DEE9FF\">                                                                                                    <\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">tests\/test_bank_account.py<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">.<\/span><span style=\"color: #D8DEE9FF\">                                                                                  [100%]<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">============================== <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">passed<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.01<\/span><span style=\"color: #A3BE8C\">s<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">==============================<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We need to talk a second about test discovery.  Notice that we did not tell <code>pytest<\/code> where to look for the <code>test_bank_account_default_balance_is_zero<\/code> function or even that the function is a test at all.  By convention, <code>pytest<\/code> will look for tests in Python files that begin with <code>test_<\/code> or end with <code>_test<\/code>.  Thus <code>pytest<\/code> looked in <code>test_bank_account.py<\/code>.  It also would have looked in <code>bank_account_test.py<\/code> as the <code>_test<\/code> is also a valid test file.  Inside of the test file, by convention, <code>pytest<\/code> will run functions that begin with <code>test_<\/code> and take no parameters.  And as you can see on the last line of the output, 1 test was discovered and it passed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s write a failing test.  Add the following test function to <code>test_bank_account.py<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"def test_failing():\n  bank_account = BankAccount(&quot;Account Owner&quot;)\n  bank_account.deposit(1000.0)\n  assert bank_account.check_balance() == 0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">test_failing<\/span><span style=\"color: #ECEFF4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  bank_account <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">BankAccount<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Account Owner<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">deposit<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">1000.0<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #81A1C1\">assert<\/span><span style=\"color: #D8DEE9FF\"> bank_account<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">check_balance<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Run pytest again.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ pytest\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pytest<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">I won&#8217;t include all of the output but the first indication that there is a failing test is this line<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/tests\/test_bank_account.py .F<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>F<\/code> means a test has failed.  Notice that the dot is for the passing test still in the file.  There is also more details about the failing test.  This shows us that the test failed on line 13.  And then the short summary.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FAILED tests\/test_bank_account.py::test_failing - assert 1000.0 == 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, the <code>test_failing<\/code> function failed at the assert because the expected value was 0 but the actual value is 1000 and thus the condition evaluates to <code>False<\/code>. To make the test pass, merely charge the equals operator to the not equals operator so the condition will evaluate to <code>True<\/code> and run <code>pytest<\/code> again. But let&#8217;s do that with the Visual Studio Code Python test runner.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Running Python Tests in Visual Studio Code<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When you install the Python extension, you also get tools for managing and running tests. It works with both <code>pytest<\/code> and <code>unittest<\/code>. Here you will see how to use it with the <code>pytest<\/code> module. Click on the <strong>Testing<\/strong> icon in the sidebar.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"692\" height=\"754\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-09-36.png\" alt=\"\" class=\"wp-image-345\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-09-36.png 692w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-09-36-275x300.png 275w\" sizes=\"auto, (max-width: 692px) 100vw, 692px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Click on the <strong>Configure Python Tests<\/strong> button.  The Command Pallete will ask you to select which testing framework you are using. Select <strong>pytest<\/strong> from the list.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"223\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-10-36-1024x223.png\" alt=\"\" class=\"wp-image-346\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-10-36-1024x223.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-10-36-300x65.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-10-36-768x167.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-10-36.png 1220w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The next prompt asks you to select the directory containing the tests.  Select <code>src<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"152\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-11-48-1024x152.png\" alt=\"\" class=\"wp-image-347\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-11-48-1024x152.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-11-48-300x44.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-11-48-768x114.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-11-48.png 1216w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Look in the Testing panel in the sidebar.  Visual Studio Code has detected the tests using the discovery rules for pytest.  To see them, expand <strong>codespaces-blank, src, test<\/strong>, and then <strong>test_bank_account.py<\/strong>. You will see the two test functions.  Click the Run Tests button at the top of the panel to run the tests.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"730\" height=\"482\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-14-42.png\" alt=\"\" class=\"wp-image-348\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-14-42.png 730w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-14-42-300x198.png 300w\" sizes=\"auto, (max-width: 730px) 100vw, 730px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The tests will run and you&#8217;ll be able to see the results in the <strong>Testing<\/strong> panel.  Notice that the <code>test_bank_account_default_balance_is_zero<\/code> test passed while the <code>test_failing<\/code> test failed.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"500\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-16-16.png\" alt=\"\" class=\"wp-image-349\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-16-16.png 740w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-16-16-300x203.png 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To see details about the failing test, click on it.  The function for the test will be highlighted and the <code>pytest<\/code> output will be displayed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"530\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-1024x530.png\" alt=\"\" class=\"wp-image-350\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-1024x530.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-300x155.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-768x397.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-1536x795.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-18-52-2048x1060.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To fix the test, change the 0 in line 14 to 1000.0.  Now you can rerun the test.  Instead of running all of the tests, including the passing tests, again right click on the failing test and select <strong>Run Test<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"754\" height=\"500\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-21-24.png\" alt=\"\" class=\"wp-image-351\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-21-24.png 754w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-21-24-300x199.png 300w\" sizes=\"auto, (max-width: 754px) 100vw, 754px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And now all the tests are passing and green!  <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"752\" height=\"482\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-22-31.png\" alt=\"\" class=\"wp-image-352\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-22-31.png 752w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/2025-01-23_22-22-31-300x192.png 300w\" sizes=\"auto, (max-width: 752px) 100vw, 752px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">There is much more you can do with pytest and the Visual Studio Code test runner.  Stay tuned for future posts to learn more.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Summary<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In this post you learned how to get started testing Python applications using Visual Studio Code.  You used the <code>pytest<\/code> framework but you can also use <code>unittest<\/code> from the Python Standard Library.  You saw how to organize the code for testing.  You wrote a few simple tests and using the command line <code>pytest<\/code> utility to run them.  Then you saw how to set up Visual Studio Code to discover tests.  You ran them using the test runner.  The failing test was fixed and then run by itself.  And you made all of the tests pass and saw this in the Visual Studio Code <strong>Testing<\/strong> panel displaying green checks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We try hard to write code that is free of bugs. But it&#8217;s impossible to&#8230;<\/p>\n","protected":false},"author":1,"featured_media":354,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18],"tags":[40,4,24],"class_list":["post-338","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-pytest","tag-python","tag-vscode"],"_links":{"self":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/comments?post=338"}],"version-history":[{"count":6,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/338\/revisions"}],"predecessor-version":[{"id":365,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/338\/revisions\/365"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/media\/354"}],"wp:attachment":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/media?parent=338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/categories?post=338"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/tags?post=338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}