{"id":23432,"date":"2022-11-11T12:31:38","date_gmt":"2022-11-11T20:31:38","guid":{"rendered":"https:\/\/coderpad.io\/?p=23432"},"modified":"2023-06-05T13:52:01","modified_gmt":"2023-06-05T20:52:01","slug":"testing-in-python-types-of-tests-and-how-to-write-them","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/testing-in-python-types-of-tests-and-how-to-write-them\/","title":{"rendered":"Testing in Python: Types of Tests and How to Write Them"},"content":{"rendered":"\n<p>You\u2019ve <em>finally <\/em>gotten your API set up. Now other applications can talk to your fully functioning REST API. Now what? What\u2019s the next step? Making sure it works! How do you know if your <a href=\"https:\/\/coderpad.io\/blog\/development\/everything-you-need-to-know-to-map-sql-tables-to-rest-endpoints-in-python\/\">REST API<\/a> is functioning <em>correctly<\/em>? Well, naturally, you test it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why is testing important?<\/h2>\n\n\n\n<p>When you hear a developer refer to testing, we\u2019re typically talking about <em>automated<\/em> testing. You may have done testing by manually making calls to your API using a tool such as Postman, Insomnia, or cURL. This works, but every time you go to change something then you have to go back and test that nothing else broke from your change.<\/p>\n\n\n\n<p>Now, imagine you have a <em>massive<\/em> REST API that\u2019s hundreds of thousands of lines long. Think about doing that same manual testing on every endpoint for every change. My gut reaction is to cringe because I don\u2019t want to do that much busy work.&nbsp;<\/p>\n\n\n\n<p>Manually testing every change just doesn\u2019t scale to a massive API. You don\u2019t want to spend hours testing just because you changed a utility file. Or, even worse, you don\u2019t want to break something and not even know it.<\/p>\n\n\n\n<p>The solution to these problems is to automate all your manual testing, so that you can verify your API&#8217;s functionality significantly faster. This also removes the risk of forgetting edge cases if you have all use cases safely documented in existing tests.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Types of testing<\/h2>\n\n\n\n<p>Automated tests come in many types and flavors. The different types of tests can be used as general guidelines and help you make sure you\u2019re writing comprehensive tests. The two most common types of tests are <strong>unit tests<\/strong> and <strong>integration tests<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Unit tests<\/h3>\n\n\n\n<p>Unit tests ideally test a small unit (or rather, amount) of code. This can often mean one function or one <code>if<\/code> statement. What level of granularity you want in your tests depends largely on what could reasonably go wrong in code you\u2019ve written.&nbsp;<\/p>\n\n\n\n<p>Your unit tests should be aimed at testing one unit of your code. Your code doing one, tiny, <em>action<\/em>is what you want to test. For example, you want to test that your <code>pet_the_cat<\/code> function returns the correct reaction from an imaginary cat. Given a class like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> requests\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Optional\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Cat<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, cat: str)<\/span>:<\/span>\n        self.cat = cat\n        self.fed = <span class=\"hljs-number\">0<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">ask_what_to_do<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; dict:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> requests.get(<span class=\"hljs-string\">\"http:\/\/www.boredapi.com\/api\/activity\"<\/span>).json()\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">pet_the_cat<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; Optional&#91;str]:<\/span>\n        actions = {\n            <span class=\"hljs-string\">\"catname\"<\/span>: <span class=\"hljs-string\">\"action\"<\/span>,\n            <span class=\"hljs-string\">\"bean\"<\/span>: <span class=\"hljs-string\">\"purr\"<\/span>,\n            <span class=\"hljs-string\">\"hades\"<\/span>: <span class=\"hljs-string\">\"scratch\"<\/span>,\n        }\n        <span class=\"hljs-keyword\">return<\/span> actions.get(self.cat)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You could either manually call your <code>pet_the_cat<\/code> function a bunch of times, giving it different input, OR you could write a unit test that look like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> cat <span class=\"hljs-keyword\">import<\/span> Cat\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">test_pet_the_cat<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n    tests = {\n        <span class=\"hljs-comment\"># \"input_parameter\": \"expected_result\",<\/span>\n        <span class=\"hljs-string\">\"catname\"<\/span>: <span class=\"hljs-string\">\"action\"<\/span>,\n        <span class=\"hljs-string\">\"not_there\"<\/span>: <span class=\"hljs-literal\">None<\/span>,\n        <span class=\"hljs-number\">1<\/span>: <span class=\"hljs-literal\">None<\/span>,\n    }\n    failed = <span class=\"hljs-number\">0<\/span>\n    <span class=\"hljs-keyword\">for<\/span> input_value, expected_result <span class=\"hljs-keyword\">in<\/span> tests.items():\n        cat = Cat(input_value)\n        output = cat.pet_the_cat()\n        <span class=\"hljs-keyword\">if<\/span> output != expected_result:\n            print(<span class=\"hljs-string\">\"FAILED: '{}' returned '{}'\"<\/span>.format(\n                input_value, output,\n            ))\n            failed += <span class=\"hljs-number\">1<\/span>\n\n    print(<span class=\"hljs-string\">\"{}\/{} Tests succeeded\"<\/span>.format(\n        len(tests) - failed, len(tests),\n    ))\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    test_pet_the_cat()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, when you want to add more test cases, you add them to the <code>tests<\/code> dict, and run all of them by running the test functions once. Now you don&#8217;t have to remember all your test cases, and you don&#8217;t have to spend time running them manually.<\/p>\n\n\n\n<p>The most fundamental rule of unit tests is that they should test only one unit of code at a time. Just like how in the example the <code>feed_the_cat<\/code> function is tested individually.<\/p>\n\n\n\n<p>It\u2019s also important to note that, as part of testing <em>small<\/em>units of code, you also don\u2019t want to rely on any other service (like a database). Your unit tests should all be able to run independent of each other (and in parallel) and not have dependencies on other services. This is why <code>ask_what_to_do<\/code> isn\u2019t tested in this example: because it has an external dependency (the API call).<\/p>\n\n\n\n<p>While we can test small bits of code, you usually don\u2019t need to write unit tests for built-in functions. After all, <em>why<\/em> test that <code>1+1<\/code> is <code>2<\/code>? You can trust that the addition functionality built into your language is trustworthy. Also, you probably don\u2019t need to write tests for third-party code that is well tested and used. Standard code used by everyone<em>should<\/em> have its own tests.<\/p>\n\n\n\n<p>You also want comprehensive code coverage, meaning you want every line of your code to be used in at least one test. This means in an <code>if \u2026 else<\/code> code block there is a unit test for the <code>if<\/code> part and a unit test for the <code>else<\/code> part.<\/p>\n\n\n\n<p>There are a lot of tools out there that can help you measure code coverage to ensure that every possible code path is covered. For Python, I\u2019m partial to using the tool <a href=\"https:\/\/coverage.readthedocs.io\/\" target=\"_blank\" rel=\"noopener\">Coverage.py<\/a>, but there are different options for different languages. For example, <a href=\"https:\/\/istanbul.js.org\/\" target=\"_blank\" rel=\"noopener\">Istanbul<\/a> is a popular tool for JavaScript, but I\u2019ve seen plenty of people use <a href=\"https:\/\/docs.cypress.io\/guides\/tooling\/code-coverage\" target=\"_blank\" rel=\"noopener\">Cypress\u2019s code coverage plugin<\/a>.<\/p>\n\n\n\n<p>Ideally, because unit tests are cheap (in cost and time, partially due to the ability to parallelize them) to run, you want total code coverage using unit tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Integration tests<\/h3>\n\n\n\n<p>Of course, you still need to test how all the units of code you just unit tested work together. Enter integration tests, which specifically test how units of code interact together.<\/p>\n\n\n\n<p>It\u2019s worth noting that integration tests can have dependencies on things such as databases, and as a result are often slower to run. Ideally they should be written so that they can run in parallel,&nbsp; so even if they have dependencies on databases they\u2019ll use their own (probably temporary) database table.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> cat <span class=\"hljs-keyword\">import<\/span> Cat\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">test_ask_what_to_do<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n    cat = Cat(<span class=\"hljs-string\">\"cat\"<\/span>)\n    runs = <span class=\"hljs-number\">2<\/span>\n    tests_passed = <span class=\"hljs-number\">0<\/span>\n    <span class=\"hljs-keyword\">for<\/span> _ <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">2<\/span>):\n        <span class=\"hljs-keyword\">try<\/span>:\n            result = cat.ask_what_to_do()\n            <span class=\"hljs-keyword\">assert<\/span> isinstance(result, dict)\n            <span class=\"hljs-keyword\">assert<\/span> <span class=\"hljs-string\">\"activity\"<\/span> <span class=\"hljs-keyword\">in<\/span> result\n            <span class=\"hljs-comment\"># Since the result is not always the same we<\/span>\n            <span class=\"hljs-comment\"># can't test everything about the result, but it's<\/span>\n            <span class=\"hljs-comment\"># an external dependency that has its own tests<\/span>\n            tests_passed += <span class=\"hljs-number\">1<\/span>\n        <span class=\"hljs-keyword\">except<\/span>:\n            <span class=\"hljs-keyword\">continue<\/span>\n\n    print(<span class=\"hljs-string\">\"{}\/{} Tests succeeded\"<\/span>.format(\n        tests_passed, runs,\n    ))\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    test_ask_what_to_do()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Other test types<\/h3>\n\n\n\n<p>There are many more types of tests, such as <a href=\"https:\/\/www.ibm.com\/garage\/method\/practices\/code\/contract-driven-testing\/\" target=\"_blank\" rel=\"noopener\">contract tests<\/a> and <a href=\"https:\/\/www.geeksforgeeks.org\/difference-between-unit-functional-acceptance-and-integration-tests\/\" target=\"_blank\" rel=\"noopener\">functional tests<\/a> (often confused with integration tests!), but unit and integration tests are far and away the most important tests to start with.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to write tests<\/h2>\n\n\n\n<p><strong>The first and most important step to writing tests is to write testable code.<\/strong> This might seem obvious, but if you don\u2019t do it the first time you write your code, then you\u2019ll probably have to refactor it. The alternative is to have tests that are a nightmare to read and implement.<\/p>\n\n\n\n<p>What does writing testable code mean, though? Testable code is often best thought about from the unit test level first \u2013 can you call a small unit of code? If you have a function that\u2019s hundreds of lines long, that\u2019s probably not very testable. After all, how do you, using automation, isolate part of the function and run only that?<\/p>\n\n\n\n<p>For example, looking at the following code snippet, how would you write unit tests for this?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> requests\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Optional\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Cat<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, cat: str)<\/span>:<\/span>\n        self.cat = cat\n        self.fed = <span class=\"hljs-number\">0<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">pet_the_cat<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; Optional&#91;str]:<\/span>\n        found_pet = requests.get(<span class=\"hljs-string\">\"https:\/\/petstore.swagger.io\/v2\/pet\/1\"<\/span>).json()\n        <span class=\"hljs-keyword\">if<\/span> found_pet.get(<span class=\"hljs-string\">\"message\"<\/span>) == <span class=\"hljs-string\">\"Pet Not Found\"<\/span>:\n            self.cat = <span class=\"hljs-string\">\"UNKNOWN\"<\/span>\n\n        actions = {\n            <span class=\"hljs-string\">\"catname\"<\/span>: <span class=\"hljs-string\">\"action\"<\/span>,\n            <span class=\"hljs-string\">\"bean\"<\/span>: <span class=\"hljs-string\">\"purr\"<\/span>,\n            <span class=\"hljs-string\">\"hades\"<\/span>: <span class=\"hljs-string\">\"scratch\"<\/span>,\n            <span class=\"hljs-string\">\"diana\"<\/span>: <span class=\"hljs-string\">\"sleep\"<\/span>,\n        }\n        <span class=\"hljs-keyword\">if<\/span> self.fed % <span class=\"hljs-number\">2<\/span> == <span class=\"hljs-number\">0<\/span> <span class=\"hljs-keyword\">and<\/span> self.cat == <span class=\"hljs-string\">\"diana\"<\/span>:\n            self.fed += <span class=\"hljs-number\">1<\/span>\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"fed\"<\/span>\n\n        <span class=\"hljs-keyword\">return<\/span> actions.get(self.cat)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The answer is: You don&#8217;t. It would be difficult. Instead, you want to take that same code, and make it testable:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> requests\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Optional\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Cat<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, cat: str)<\/span>:<\/span>\n        self.cat = cat\n        self.fed = <span class=\"hljs-number\">0<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">feed_the_cat<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; Optional&#91;str]:<\/span>\n        <span class=\"hljs-keyword\">if<\/span> self.fed % <span class=\"hljs-number\">2<\/span> == <span class=\"hljs-number\">0<\/span> <span class=\"hljs-keyword\">and<\/span> self.cat == <span class=\"hljs-string\">\"diana\"<\/span>:\n            self.fed += <span class=\"hljs-number\">1<\/span>\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"fed\"<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_cat_action<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; str:<\/span>\n        actions = {\n            <span class=\"hljs-string\">\"catname\"<\/span>: <span class=\"hljs-string\">\"action\"<\/span>,\n            <span class=\"hljs-string\">\"bean\"<\/span>: <span class=\"hljs-string\">\"purr\"<\/span>,\n            <span class=\"hljs-string\">\"hades\"<\/span>: <span class=\"hljs-string\">\"scratch\"<\/span>,\n            <span class=\"hljs-string\">\"diana\"<\/span>: <span class=\"hljs-string\">\"sleep\"<\/span>,\n        }\n        <span class=\"hljs-keyword\">return<\/span> actions.get(self.cat)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">find_the_pet<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; <span class=\"hljs-keyword\">None<\/span>:<\/span>\n        found_pet = requests.get(<span class=\"hljs-string\">\"https:\/\/petstore.swagger.io\/v2\/pet\/1\"<\/span>).json()\n        <span class=\"hljs-keyword\">if<\/span> found_pet.get(<span class=\"hljs-string\">\"message\"<\/span>) == <span class=\"hljs-string\">\"Pet Not Found\"<\/span>:\n            self.cat = <span class=\"hljs-string\">\"UNKNOWN\"<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">pet_the_cat<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; Optional&#91;str]:<\/span>\n        self.find_the_cat()\n\n        fed = self.feed_the_cat()\n        <span class=\"hljs-keyword\">if<\/span> fed <span class=\"hljs-keyword\">is<\/span> <span class=\"hljs-keyword\">not<\/span> <span class=\"hljs-literal\">None<\/span>:\n            <span class=\"hljs-keyword\">return<\/span> fed\n\n        <span class=\"hljs-keyword\">return<\/span> self.get_cat_action()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, you can write separate unit tests for each function without dependencies and an integration test for the one that has dependencies.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ok, I have unit testable code. Now make it integration testable.<\/h3>\n\n\n\n<p>After you have code that\u2019s easy to unit test, think about your code at the integration test level. Are there clear lines, and ideally \u2018contracts\u2019 between modules of code?<\/p>\n\n\n\n<p>For instance, earlier in our <code>Cat<\/code> example we had an <code>ask_what_to_do<\/code> function that returns different results on every call. However, are there things we know we can expect to be true about the data returned by that function?<\/p>\n\n\n\n<p>Yes! We know the key names to expect in an error or a success. Because of this, we can test what we <em>do<\/em> know about the results:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">assert<\/span> isinstance(result, dict)\n<span class=\"hljs-keyword\">assert<\/span> <span class=\"hljs-string\">\"activity\"<\/span> <span class=\"hljs-keyword\">in<\/span> result<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>On the other hand, if we expected an error, we could have our test assert the keys expected:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">assert<\/span> isinstance(result, dict)\n<span class=\"hljs-keyword\">assert<\/span> \u201cerror\u201d <span class=\"hljs-keyword\">in<\/span> result\n<span class=\"hljs-keyword\">assert<\/span> \u201cactivity\u201d <span class=\"hljs-keyword\">not<\/span> <span class=\"hljs-keyword\">in<\/span> result<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote\">\n<p>\u2139\ufe0f Notice that the unit tests shown here can run without anything other than the file and python, while the integration tests cannot. This is because the unit tests don\u2019t depend on the REST API to be something we can actually \u2018hit\u2019 or \u2018call\u2019 \u2013 it tests the underlying code. Meanwhile, integration tests try to actually call the API, so if the API isn\u2019t being hosted in some way then the integration tests cannot run.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Testing code that has dependencies<\/h3>\n\n\n\n<p>So, we\u2019ve specified that unit tests shouldn\u2019t call to external dependencies, but it\u2019s just a fact that code is going to have external dependencies, like our database, and network calls, like making a network call to the API.<\/p>\n\n\n\n<p>So, how do we write unit tests for Python code that relies on dependencies? There are lots of options, but we\u2019re going to talk about two common ones &#8211; <strong>unittest<\/strong> and <strong>pytest<\/strong>.&nbsp;<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p>\u2139\ufe0f Learn how you can test for code that has dependencies in <a href=\"https:\/\/coderpad.io\/blog\/development\/a-guide-to-database-unit-testing-with-pytest-and-sqlalchemy\/\">this guide to database unit testing with pytest and SQLAlchemy<\/a>.<\/p>\n<\/blockquote>\n\n\n\n<p><strong>Unittest<\/strong> is built into Python and has all the basic things you\u2019ll need to write tests. <strong>Pytest<\/strong> is often used in conjunction with <strong>unittest<\/strong>, as they share a fair bit of functionality. It\u2019s fair to think of <strong>pytest<\/strong> as a tool designed to make writing and running tests easier. In particular, <strong>pytest<\/strong> enables functionality like running tests in parallel. Meanwhile <strong>unittest<\/strong> is a tool designed to enable writing tests, but it, by default, does not have the ability to run tests in parallel.<\/p>\n\n\n\n<p>Using these tools, we can switch out dependencies with code that make it easier to test without modifying our source code. There are two ways to do this: dependency injection and mocking. In my experience people tend to be passionate about one or the other.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Dependency injection<\/strong><\/h4>\n\n\n\n<p>Dependency injection is a complex topic, but here&#8217;s the gist: imagine writing your code in such a way that it\u2019s possible to pass in different code to any calls to external dependencies during test time, allowing for a cleaner unit test. In other words, you \u2018inject\u2019 a fake dependency, making the code not call out to an external dependency.<\/p>\n\n\n\n<p>To illustrate, let&#8217;s imagine when I\u2019m ordering something online, and I choose to order a test item instead of a real item. The website says \u201coh, ok\u201d, and doesn\u2019t bother to charge me or send any kind of order to its database.<\/p>\n\n\n\n<p>For example, we make a small change to our <code>ask_what_to_do<\/code> function in our cat class:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Callable, Optional\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Cat<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, cat: str)<\/span>:<\/span>\n        self.cat = cat\n        self.fed = <span class=\"hljs-number\">0<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">ask_what_to_do<\/span><span class=\"hljs-params\">(self, request_function: Callable)<\/span> -&gt; dict:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> request_function(<span class=\"hljs-string\">\"http:\/\/www.boredapi.com\/api\/activity\"<\/span>).json()\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">pet_the_cat<\/span><span class=\"hljs-params\">(self)<\/span> -&gt; Optional&#91;str]:<\/span>\n        actions = {\n            <span class=\"hljs-string\">\"catname\"<\/span>: <span class=\"hljs-string\">\"action\"<\/span>,\n            <span class=\"hljs-string\">\"bean\"<\/span>: <span class=\"hljs-string\">\"purr\"<\/span>,\n            <span class=\"hljs-string\">\"hades\"<\/span>: <span class=\"hljs-string\">\"scratch\"<\/span>,\n        }\n        <span class=\"hljs-keyword\">return<\/span> actions.get(self.cat)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In <code>ask_what_to_do<\/code>, rather than directly calling <code>requests.get<\/code> we call <code>request_function<\/code> which is an argument we pass in. Now, we have control over what <code>request_function<\/code> is, rather than it always being a call out to <code>requests.get<\/code> (a dependency). Thus we can pass in a different function <code>fake_request_function<\/code> which does not call out to an external dependency, thus <em>injecting<\/em> the dependency.<\/p>\n\n\n\n<p>And now, because we\u2019re injecting the dependency, we can write a unit test for the function, <code>ask_what_to_do<\/code>, that previously had a dependency:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> cat_1 <span class=\"hljs-keyword\">import<\/span> Cat\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FakeFailResult<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">json<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> {}\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FakeSuccessResult<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">json<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> {\n            <span class=\"hljs-string\">\"activity\"<\/span>: <span class=\"hljs-string\">\"Organize your pantry\"<\/span>,\n            <span class=\"hljs-string\">\"type\"<\/span>:<span class=\"hljs-string\">\"busywork\"<\/span>,\n            <span class=\"hljs-string\">\"participants\"<\/span>:<span class=\"hljs-number\">1<\/span>,\n            <span class=\"hljs-string\">\"price\"<\/span>:<span class=\"hljs-number\">0<\/span>,\n            <span class=\"hljs-string\">\"link\"<\/span>:<span class=\"hljs-string\">\"\"<\/span>,\n            <span class=\"hljs-string\">\"key\"<\/span>:<span class=\"hljs-string\">\"3954882\"<\/span>,\n            <span class=\"hljs-string\">\"accessibility\"<\/span>:<span class=\"hljs-number\">0<\/span>,\n        }\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">fake_request_function<\/span><span class=\"hljs-params\">(url)<\/span> -&gt; dict:<\/span>\n    <span class=\"hljs-keyword\">if<\/span> url != <span class=\"hljs-string\">\"http:\/\/www.boredapi.com\/api\/activity\"<\/span>:\n        <span class=\"hljs-keyword\">return<\/span> FakeFailResult()\n\n    <span class=\"hljs-keyword\">return<\/span> FakeSuccessResult()\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">test_ask_what_to_do<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n    <span class=\"hljs-comment\"># Since all `ask_what_to_do` does is make this request, we can easily test<\/span>\n    <span class=\"hljs-comment\"># that nothing has been accidentally added<\/span>\n    cat = Cat(<span class=\"hljs-string\">\"catname\"<\/span>)\n    output = cat.ask_what_to_do(fake_request_function)\n    <span class=\"hljs-keyword\">assert<\/span> output == FakeSuccessResult().json()\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    test_ask_what_to_do()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote\">\n<p>\ud83d\udca1 Dependency injection has more code up-front so it doesn\u2019t make as much sense on a small scale, but it gains a lot more value when you get to the scale of classes and multiple things being passed around or called.<\/p>\n<\/blockquote>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Mocking<\/strong><\/h4>\n\n\n\n<p>Mocking (and no, I\u2019m not mocking you\u2013it\u2019s actually called mocking) is about hijacking a call, be it a function call, or a class, or whatever, and giving back fake output. So, the external dependency is never called, even though the code thinks it was.<\/p>\n\n\n\n<p>This is a lot like if you told me to do something, and I told you I did it, even though I never did. I gave you the output of \u201cI did it\u201d, but I never had to actually get up and go do something, so you didn\u2019t have to wait for me to finish doing some action.<\/p>\n\n\n\n<p>This makes a lot of sense on a small scale because it\u2019s not much code, but at scale it gets unwieldy quickly. As you can imagine, you don\u2019t want 500 mocks for one test.<\/p>\n\n\n\n<p>A quick example:<\/p>\n\n\n\n<p>Using our original implementation of Cat:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> unittest <span class=\"hljs-keyword\">import<\/span> mock\n<span class=\"hljs-keyword\">from<\/span> cat <span class=\"hljs-keyword\">import<\/span> Cat\n\n\n<span class=\"hljs-meta\">@mock.patch(\"cat.requests\")<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">test_ask_what_to_do<\/span><span class=\"hljs-params\">(mocked_requests)<\/span>:<\/span>\n    mocked_result = mock.MagicMock()\n    mocked_requests.get.return_value = mocked_result\n\n    cat = Cat(<span class=\"hljs-string\">\"catname\"<\/span>)\n    output = cat.ask_what_to_do()\n    <span class=\"hljs-keyword\">assert<\/span> output == mocked_result.json()\n    mocked_requests.get.assert_called_once_with(\n        <span class=\"hljs-string\">\"http:\/\/www.boredapi.com\/api\/activity\"<\/span>\n    )\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    test_ask_what_to_do()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Within Python mocks there are different types\u2013<code>Mock<\/code>s, <code>MagicMock<\/code>s, <code>AsyncMocks<\/code>\u2013all with their own specific uses (there are also things called fixtures and different tools you can use for testing), but I\u2019ll save those for another blog post.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Mocking vs dependency injection<\/strong><\/h4>\n\n\n\n<p>The reality is that \u2018mocking\u2019 is typically better for small scale projects without a lot of dependencies, because it doesn\u2019t require a lot of work up front. However, mocks are not very reusable, so they don\u2019t scale well.<\/p>\n\n\n\n<p>Meanwhile, \u2018dependency injection\u2019 is better for large scale projects, but typically requires more up front work setting up all the fake dependencies. After you have dependency injection setup it\u2019s easier to reuse fake dependencies across your code base, though, so it can be great for scaling.<\/p>\n\n\n\n<p>In my experience these two concepts can be polarizing because they result in fundamentally different programming styles &#8211; where do you put the dependencies, how are they called, etcetera. That said, one style can be converted to another, so which you should use is a question based on either:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If the decision is being made after the code is written, then it should be made based on the style of existing code.<\/li>\n\n\n\n<li>Scale of planned development of the code. For example: will this be a concept reused elsewhere in your project? A <code>User<\/code> object is always going to be reused, so it\u2019s something that, depending on the size of your project, is probably worth creating a fake instance of and <em>injecting<\/em>.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">TL;DR<\/h2>\n\n\n\n<p>There are oodles and oodles of ways to test, and while that can be fun to dive into, the things you <em>really<\/em> need to know about testing are <em>how to<\/em> and <em>when to<\/em> use <strong>unit testing<\/strong> and <strong>integration testing<\/strong>.&nbsp;<\/p>\n\n\n\n<p>Unit testing tests all the small pieces of code, cheaply and quickly because it doesn\u2019t rely on any service (for example, an API) running. Meanwhile integration testing tests if all those units of code work together and is more expensive due to dependencies like database time.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p>\ud83d\udca1 <strong>Pro tip<\/strong>: Write testable code and tests in interviews! It can really set you apart.<\/p>\n<\/blockquote>\n\n\n\n<p>For a more front-end focused take on testing you can check out <a href=\"https:\/\/coderpad.io\/blog\/development\/integration-tests-vs-unit-tests-integration-matters-more\/\">CoderPad\u2019s blog on testing types<\/a>, oriented at front-end development. Different tests are better for different areas of code. It\u2019s always good to bear in mind <em>what<\/em> you\u2019re testing, after all.<\/p>\n\n\n\n<p><em><a href=\"https:\/\/www.linkedin.com\/in\/jennifer-vannier\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jennifer<\/a> is a full stack developer with a passion for all areas of software development. He loves being a polyglot of programming languages and teaching others what he&#8217;s learned.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Manually testing every change doesn&#8217;t scale to a massive API. Automating your tests improves the scale of testing your application and allows you to verify your API&#8217;s functionality faster. Learn what testing is, the type of tests, and how to write them in Python.<\/p>\n","protected":false},"author":1,"featured_media":23652,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[37],"keyword-cluster":[],"class_list":["post-23432","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/23432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=23432"}],"version-history":[{"count":27,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/23432\/revisions"}],"predecessor-version":[{"id":24997,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/23432\/revisions\/24997"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/23652"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=23432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=23432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=23432"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=23432"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=23432"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=23432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}