Skip to content

Commit 8293d71

Browse files
add pytest-mock
1 parent c5bf8aa commit 8293d71

File tree

4 files changed

+524
-42
lines changed

4 files changed

+524
-42
lines changed

Chapter5/testing.ipynb

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,187 @@
26592659
"By using mocks in these ways, we can create more reliable and controlled unit tests for our data projects, ensuring that our code behaves correctly under various conditions."
26602660
]
26612661
},
2662+
{
2663+
"cell_type": "markdown",
2664+
"id": "3e516385",
2665+
"metadata": {},
2666+
"source": [
2667+
"### pytest-mock vs unittest.mock: Simplifying Mocking in Python Tests"
2668+
]
2669+
},
2670+
{
2671+
"cell_type": "code",
2672+
"execution_count": null,
2673+
"id": "d6041e6a",
2674+
"metadata": {
2675+
"tags": [
2676+
"hide-cell"
2677+
]
2678+
},
2679+
"outputs": [],
2680+
"source": [
2681+
"!pip install pytest-mock"
2682+
]
2683+
},
2684+
{
2685+
"cell_type": "markdown",
2686+
"id": "350f5aa4",
2687+
"metadata": {},
2688+
"source": [
2689+
"Traditional mocking with unittest.mock often requires repetitive setup and teardown code, which can make test code harder to read and maintain. \n",
2690+
"\n",
2691+
"pytest-mock addresses this issue by leveraging pytest's fixture system, simplifying the mocking process and reducing boilerplate code.\n",
2692+
"\n",
2693+
"Consider the following example that demonstrates the difference between unittest.mock and pytest-mock.\n",
2694+
"\n",
2695+
"Using unittest.mock:"
2696+
]
2697+
},
2698+
{
2699+
"cell_type": "code",
2700+
"execution_count": 5,
2701+
"id": "ed814822",
2702+
"metadata": {},
2703+
"outputs": [
2704+
{
2705+
"name": "stdout",
2706+
"output_type": "stream",
2707+
"text": [
2708+
"Overwriting test_rm_file.py\n"
2709+
]
2710+
}
2711+
],
2712+
"source": [
2713+
"%%writefile test_rm_file.py\n",
2714+
"from unittest.mock import patch\n",
2715+
"import os\n",
2716+
"\n",
2717+
"\n",
2718+
"def rm_file(filename):\n",
2719+
" os.remove(filename)\n",
2720+
"\n",
2721+
"\n",
2722+
"def test_with_unittest_mock():\n",
2723+
" with patch(\"os.remove\") as mock_remove:\n",
2724+
" rm_file(\"file\")\n",
2725+
" mock_remove.assert_called_once_with(\"file\")"
2726+
]
2727+
},
2728+
{
2729+
"cell_type": "code",
2730+
"execution_count": 4,
2731+
"id": "2e9d8b1d",
2732+
"metadata": {},
2733+
"outputs": [
2734+
{
2735+
"name": "stdout",
2736+
"output_type": "stream",
2737+
"text": [
2738+
"\u001b[1m============================= test session starts ==============================\u001b[0m\n",
2739+
"platform darwin -- Python 3.11.2, pytest-7.4.3, pluggy-1.3.0\n",
2740+
"rootdir: /Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter5\n",
2741+
"plugins: dvc-3.28.0, hydra-core-1.3.2, typeguard-4.1.5, mock-3.14.0, anyio-4.2.0, hypothesis-6.88.4\n",
2742+
"collected 1 item \u001b[0m\n",
2743+
"\n",
2744+
"test_rm_file.py \u001b[32m.\u001b[0m\u001b[32m [100%]\u001b[0m\n",
2745+
"\n",
2746+
"\u001b[32m============================== \u001b[32m\u001b[1m1 passed\u001b[0m\u001b[32m in 0.01s\u001b[0m\u001b[32m ===============================\u001b[0m\n",
2747+
"\u001b[0m"
2748+
]
2749+
}
2750+
],
2751+
"source": [
2752+
"!pytest test_rm_file.py"
2753+
]
2754+
},
2755+
{
2756+
"cell_type": "markdown",
2757+
"id": "0607964b",
2758+
"metadata": {},
2759+
"source": [
2760+
"Using pytest-mock:"
2761+
]
2762+
},
2763+
{
2764+
"cell_type": "code",
2765+
"execution_count": 3,
2766+
"id": "2e772780",
2767+
"metadata": {},
2768+
"outputs": [
2769+
{
2770+
"name": "stdout",
2771+
"output_type": "stream",
2772+
"text": [
2773+
"Writing test_rm_file.py\n"
2774+
]
2775+
}
2776+
],
2777+
"source": [
2778+
"%%writefile test_rm_file.py\n",
2779+
"import os\n",
2780+
"\n",
2781+
"\n",
2782+
"def rm_file(filename):\n",
2783+
" os.remove(filename)\n",
2784+
"\n",
2785+
"\n",
2786+
"def test_unix_fs(mocker):\n",
2787+
" mocker.patch(\"os.remove\")\n",
2788+
" rm_file(\"file\")\n",
2789+
" os.remove.assert_called_once_with(\"file\")"
2790+
]
2791+
},
2792+
{
2793+
"cell_type": "code",
2794+
"execution_count": 8,
2795+
"id": "f74a6fca",
2796+
"metadata": {},
2797+
"outputs": [
2798+
{
2799+
"name": "stdout",
2800+
"output_type": "stream",
2801+
"text": [
2802+
"\u001b[1m============================= test session starts ==============================\u001b[0m\n",
2803+
"platform darwin -- Python 3.11.2, pytest-7.4.3, pluggy-1.3.0\n",
2804+
"rootdir: /Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter5\n",
2805+
"plugins: dvc-3.28.0, hydra-core-1.3.2, typeguard-4.1.5, mock-3.14.0, anyio-4.2.0, hypothesis-6.88.4\n",
2806+
"collected 1 item \u001b[0m\n",
2807+
"\n",
2808+
"test_rm_file.py \u001b[32m.\u001b[0m\u001b[32m [100%]\u001b[0m\n",
2809+
"\n",
2810+
"\u001b[32m============================== \u001b[32m\u001b[1m1 passed\u001b[0m\u001b[32m in 0.01s\u001b[0m\u001b[32m ===============================\u001b[0m\n",
2811+
"\u001b[0m"
2812+
]
2813+
}
2814+
],
2815+
"source": [
2816+
"!pytest test_rm_file.py"
2817+
]
2818+
},
2819+
{
2820+
"cell_type": "markdown",
2821+
"id": "4f873409",
2822+
"metadata": {},
2823+
"source": [
2824+
"Key differences:\n",
2825+
"\n",
2826+
"1. Setup: pytest-mock uses the `mocker` fixture, automatically provided by pytest, eliminating the need to import patching utilities.\n",
2827+
"\n",
2828+
"2. Patching: With pytest-mock, you simply call `mocker.patch('os.remove')`, whereas unittest.mock requires a context manager or decorator.\n",
2829+
"\n",
2830+
"3. Cleanup: pytest-mock automatically undoes mocking after the test, while unittest.mock relies on the context manager for cleanup.\n",
2831+
"\n",
2832+
"4. Accessing mocks: pytest-mock allows direct access to the patched function (e.g., `os.remove.assert_called_once_with()`), while unittest.mock requires accessing the mock through a variable (e.g., `mock_remove.assert_called_once_with()`)."
2833+
]
2834+
},
2835+
{
2836+
"cell_type": "markdown",
2837+
"id": "2ca3e0b7",
2838+
"metadata": {},
2839+
"source": [
2840+
"[Link to pytest-mock](https://bit.ly/4dBDAOE)."
2841+
]
2842+
},
26622843
{
26632844
"attachments": {},
26642845
"cell_type": "markdown",
@@ -4728,7 +4909,7 @@
47284909
"name": "python",
47294910
"nbconvert_exporter": "python",
47304911
"pygments_lexer": "ipython3",
4731-
"version": "3.11.6"
4912+
"version": "3.11.2"
47324913
},
47334914
"toc": {
47344915
"base_numbering": 1,

0 commit comments

Comments
 (0)