{"id":433,"date":"2024-01-29T11:16:50","date_gmt":"2024-01-29T11:16:50","guid":{"rendered":"https:\/\/learnpython.elegantwallp.com\/?p=433"},"modified":"2024-01-29T11:16:51","modified_gmt":"2024-01-29T11:16:51","slug":"python-patch","status":"publish","type":"post","link":"https:\/\/learnpython.elegantwallp.com\/2024\/01\/29\/python-patch\/","title":{"rendered":"Python patch()"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you\u2019ll learn how to use the Python&nbsp;<code>patch()<\/code>&nbsp;to replace a target with a mock object temporarily.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction to the Python patch<\/h2>\n\n\n\n<p>The&nbsp;<code>unittest.mock<\/code>&nbsp;module has a&nbsp;<code>patch()<\/code>&nbsp;that allows you to temporarily replace a target with a mock object.<\/p>\n\n\n\n<p>A target can be a\u00a0function, a\u00a0method, or a\u00a0class. It\u2019s a string with the following format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>'package.module.className'<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>To use the&nbsp;<code>patch()<\/code>&nbsp;correctly, you need to understand two important steps:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Identify the target<\/li>\n\n\n\n<li>How to call&nbsp;<code>patch()<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Identifying the target<\/h3>\n\n\n\n<p>To identify a target:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The target must be importable.<\/li>\n\n\n\n<li>And patch the target where it is used, not where it comes from.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Calling patch<\/h3>\n\n\n\n<p>Python provides you with three ways to call&nbsp;<code>patch()<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Decorators for a function or a class.<\/li>\n\n\n\n<li>Context manager<\/li>\n\n\n\n<li>Manual start\/stop<\/li>\n<\/ul>\n\n\n\n<p>When you use the&nbsp;<code>patch()<\/code>&nbsp;as a decorator of a function or class, inside the function or class the target is replaced with a new object.<\/p>\n\n\n\n<p>If you use the patch in a context manager, inside the&nbsp;<code>with<\/code>&nbsp;statement, the target is replaced with a new object.<\/p>\n\n\n\n<p>In both cases, when the function or the&nbsp;<code>with<\/code>&nbsp;statement exits, the patch is undone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Python patch examples<\/h2>\n\n\n\n<p>Let\u2019s create a new module called\u00a0<code>total.py<\/code>\u00a0for demonstration purposes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def read(filename): \"\"\" read a text file and return a list of numbers \"\"\" with open(filename) as f: lines = f.readlines() return &#91;float(line.strip()) for line in lines] def calculate_total(filename): \"\"\" return the sum of numbers in a text file \"\"\" numbers = read(filename) return sum(numbers)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>How it works.<\/p>\n\n\n\n<p>The\u00a0<code>read()<\/code>\u00a0function reads a text file, converts each line into a number, and returns a list of numbers. For example, a text file has the following lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>1 2 3<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>the\u00a0<code>read()<\/code>\u00a0function will return the following list:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>&#91;1, 2, 3]<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>calculate_total()<\/code>&nbsp;function uses the&nbsp;<code>read()<\/code>&nbsp;function to get a list of numbers from a file and returns the sum of the numbers.<\/p>\n\n\n\n<p>To test\u00a0<code>calculate_total()<\/code>, you can create a\u00a0<code>test_total_mock.py<\/code>\u00a0module and mock the\u00a0<code>read()<\/code>\u00a0function as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>import unittest from unittest.mock import MagicMock import total class TestTotal(unittest.TestCase): def test_calculate_total(self): total.read = MagicMock() total.read.return_value = &#91;1, 2, 3] result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Run the test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>python -m unittest test_total_mock.py -v<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>test_calculate_total (test_total_mock.TestTotal) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Instead of using the&nbsp;<code>MagicMock()<\/code>&nbsp;object directly, you can use the&nbsp;<code>patch()<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1) Using patch() as a decorator<\/h3>\n\n\n\n<p>The following test module\u00a0<code>test_total_with_patch_decorator.py<\/code>\u00a0tests the\u00a0<code>total.py<\/code>\u00a0module using the\u00a0<code>patch()<\/code>\u00a0as a function decorator:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>import unittest from unittest.mock import patch import total class TestTotal(unittest.TestCase): @patch('total.read') def test_calculate_total(self, mock_read): mock_read.return_value = &#91;1, 2, 3] result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>How it works.<\/p>\n\n\n\n<p>First, import the patch from the\u00a0<code>unittest.mock<\/code>\u00a0module:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from unittest.mock import patch<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Second, decorate the\u00a0<code>test_calculate_total()<\/code>\u00a0test method with the\u00a0<code>@patch<\/code>\u00a0decorator. The target is the read function of the total module.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>@patch('total.read') def test_calculate_total(self, mock_read): <em># ...<\/em><\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Because of the&nbsp;<code>@patch<\/code>&nbsp;decorator, the&nbsp;<code>test_calculate_total()<\/code>&nbsp;method has an additional argument mock_read which is an instance of the MagicMock.<\/p>\n\n\n\n<p>Note that you can name the parameter whatever you want.<\/p>\n\n\n\n<p>Inside the&nbsp;<code>test_calculate_total()<\/code>&nbsp;method, the&nbsp;<code>patch()<\/code>&nbsp;will replace the total.<code>read()<\/code>&nbsp;function with the mock_read object.<\/p>\n\n\n\n<p>Third, assign a list to the return_value of the mock object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code> mock_read.return_value = &#91;1, 2, 3]<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Finally, call the&nbsp;<code>calculate_total()<\/code>&nbsp;function and use the&nbsp;<code>assertEqual()<\/code>&nbsp;method to test if the total is 6.<\/p>\n\n\n\n<p>Because the mock_read object will be called instead of the total.<code>read()<\/code>\u00a0function, you can pass any filename to the\u00a0<code>calculate_total()<\/code>\u00a0function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Run the test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>python -m unittest test_total_patch_decorator -v<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>test_calculate_total (test_total_patch_decorator.TestTotal) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2) Using patch() as a context manager<\/h3>\n\n\n\n<p>The following example illustrates how to use the\u00a0<code>patch()<\/code>\u00a0as a context manager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>import unittest from unittest.mock import patch import total class TestTotal(unittest.TestCase): def test_calculate_total(self): with patch('total.read') as mock_read: mock_read.return_value = &#91;1, 2, 3] result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>How it works.<\/p>\n\n\n\n<p>First, patch\u00a0<code>total.read()<\/code>\u00a0function using as the\u00a0<code>mock_read<\/code>\u00a0object in a context manager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>with patch('total.read') as mock_read:<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>It means that within the&nbsp;<code>with<\/code>&nbsp;block, the&nbsp;<code>patch()<\/code>&nbsp;replaces the&nbsp;<code>total.read()<\/code>&nbsp;function with the mock_read object.<\/p>\n\n\n\n<p>Second, assign a list of numbers to the\u00a0<code>return_value<\/code>\u00a0property of the\u00a0<code>mock_read<\/code>\u00a0object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>mock_read.return_value = &#91;1, 2, 3]<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Third, call the\u00a0<code>calculate_total()<\/code>\u00a0function and test if the result of the\u00a0<code>calculate_total()<\/code>\u00a0function is equal 6 using the\u00a0<code>assertEqual()<\/code>\u00a0method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Run the test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>python -m unittest test_total_patch_ctx_mgr -v<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>test_calculate_total (test_total_patch_ctx_mgr.TestTotal) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3) Using patch() manually<\/h3>\n\n\n\n<p>The following test module (<code>test_total_patch_manual.py<\/code>) shows how to use\u00a0<code>patch()<\/code>\u00a0manually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>import unittest from unittest.mock import patch import total class TestTotal(unittest.TestCase): def test_calculate_total(self): <em># start patching<\/em> patcher = patch('total.read') <em># create a mock object<\/em> mock_read = patcher.start() <em># assign the return value<\/em> mock_read.return_value = &#91;1, 2, 3] <em># test the calculate_total<\/em> result = total.calculate_total('') self.assertEqual(result, 6) <em># stop patching<\/em> patcher.stop()<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>How it works.<\/p>\n\n\n\n<p>First, start a patch by calling\u00a0<code>patch()<\/code>\u00a0with a target is the\u00a0<code>read()<\/code>\u00a0function of the\u00a0<code>total<\/code>\u00a0module:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>patcher = patch('total.read')<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Next, create a mock object for the\u00a0<code>read()<\/code>\u00a0function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>mock_read = patcher.start()<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Then, assign a list of numbers to the\u00a0<code>return_value<\/code>\u00a0of the\u00a0<code>mock_read<\/code>\u00a0object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>After that, call the\u00a0<code>calculate_total()<\/code>\u00a0and test its result.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def test_calculate_total(self): self.mock_read.return_value = &#91;1, 2, 3] result = total.calculate_total('') self.assertEqual(result, 6)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Finally, stop patching by calling the\u00a0<code>stop()<\/code>\u00a0method of the patcher object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>patcher.stop()<\/code><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Summary: in this tutorial, you\u2019ll learn how to use the Python&nbsp;patch()&nbsp;to replace a target with a mock object temporarily. Introduction to the Python patch The&nbsp;unittest.mock&nbsp;module has a&nbsp;patch()&nbsp;that allows you to temporarily replace a target with a mock object. A target can be a\u00a0function, a\u00a0method, or a\u00a0class. It\u2019s a string with the following format: To use [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[43],"tags":[],"class_list":["post-433","post","type-post","status-publish","format-standard","hentry","category-3-python-unit-testing"],"_links":{"self":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/comments?post=433"}],"version-history":[{"count":1,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/433\/revisions"}],"predecessor-version":[{"id":434,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/433\/revisions\/434"}],"wp:attachment":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/media?parent=433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/categories?post=433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/tags?post=433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}