Skip to content

Commit 4d27fa4

Browse files
add mock
1 parent 1712c60 commit 4d27fa4

File tree

4 files changed

+623
-64
lines changed

4 files changed

+623
-64
lines changed

Chapter5/testing.ipynb

Lines changed: 232 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,7 @@
11511151
{
11521152
"cell_type": "code",
11531153
"execution_count": 1,
1154+
"id": "52c638a0",
11541155
"metadata": {},
11551156
"outputs": [
11561157
{
@@ -1305,13 +1306,15 @@
13051306
},
13061307
{
13071308
"cell_type": "markdown",
1309+
"id": "781ff6d9",
13081310
"metadata": {},
13091311
"source": [
13101312
"### Test for Specific Exceptions in Unit Testing"
13111313
]
13121314
},
13131315
{
13141316
"cell_type": "markdown",
1317+
"id": "8c087d03",
13151318
"metadata": {},
13161319
"source": [
13171320
"To test for a specific exception in unit testing, use `pytest.raises`.\n",
@@ -1322,6 +1325,7 @@
13221325
{
13231326
"cell_type": "code",
13241327
"execution_count": 7,
1328+
"id": "24775262",
13251329
"metadata": {},
13261330
"outputs": [
13271331
{
@@ -1353,6 +1357,7 @@
13531357
},
13541358
{
13551359
"cell_type": "markdown",
1360+
"id": "e2de71c1",
13561361
"metadata": {},
13571362
"source": [
13581363
"```bash\n",
@@ -1363,6 +1368,7 @@
13631368
{
13641369
"cell_type": "code",
13651370
"execution_count": 8,
1371+
"id": "2898168f",
13661372
"metadata": {
13671373
"tags": [
13681374
"remove-input"
@@ -2399,17 +2405,163 @@
23992405
"\n",
24002406
"A mock object can control the behavior of a real object in a testing environment by simulating responses from external services.\n",
24012407
"\n",
2402-
"The following code uses a mock object to test the `get_data` function's behavior when calling an API that may either succeed or fail."
2408+
"Here are two common use cases with examples:"
24032409
]
24042410
},
24052411
{
2406-
"attachments": {},
24072412
"cell_type": "markdown",
2408-
"id": "aa19ebb6",
2413+
"id": "fa134f51",
24092414
"metadata": {},
24102415
"source": [
2411-
"```python\n",
2416+
"1. **Mocking Time-Dependent Functions**\n",
2417+
"\n",
2418+
"When testing functions that depend on the current time or date, you can mock the time to ensure consistent results.\n",
2419+
"\n",
2420+
"Example: Testing a function that returns data for the last week"
2421+
]
2422+
},
2423+
{
2424+
"cell_type": "code",
2425+
"execution_count": 43,
2426+
"id": "25d20b5a",
2427+
"metadata": {},
2428+
"outputs": [
2429+
{
2430+
"name": "stdout",
2431+
"output_type": "stream",
2432+
"text": [
2433+
"Overwriting main.py\n"
2434+
]
2435+
}
2436+
],
2437+
"source": [
2438+
"%%writefile main.py\n",
2439+
"from datetime import datetime, timedelta\n",
2440+
"\n",
2441+
"\n",
2442+
"def get_data_for_last_week():\n",
2443+
" end_date = datetime.now().date()\n",
2444+
" start_date = end_date - timedelta(days=7)\n",
2445+
" return {\n",
2446+
" \"start_date\": start_date.strftime(\"%Y-%m-%d\"),\n",
2447+
" \"end_date\": end_date.strftime(\"%Y-%m-%d\"),\n",
2448+
" }"
2449+
]
2450+
},
2451+
{
2452+
"cell_type": "markdown",
2453+
"id": "e66a0f82",
2454+
"metadata": {},
2455+
"source": [
2456+
"Now, let's create a test for this function using mock:\n"
2457+
]
2458+
},
2459+
{
2460+
"cell_type": "code",
2461+
"execution_count": 52,
2462+
"id": "7754b84b",
2463+
"metadata": {},
2464+
"outputs": [
2465+
{
2466+
"name": "stdout",
2467+
"output_type": "stream",
2468+
"text": [
2469+
"Overwriting test_main.py\n"
2470+
]
2471+
}
2472+
],
2473+
"source": [
2474+
"%%writefile test_main.py\n",
2475+
"from datetime import datetime\n",
24122476
"from unittest.mock import patch\n",
2477+
"from main import get_data_for_last_week\n",
2478+
"\n",
2479+
"\n",
2480+
"@patch(\"main.datetime\")\n",
2481+
"def test_get_data_for_last_week(mock_datetime):\n",
2482+
" # Set a fixed date for the test\n",
2483+
" mock_datetime.now.return_value = datetime(2024, 8, 5)\n",
2484+
"\n",
2485+
" # Call the function\n",
2486+
" result = get_data_for_last_week()\n",
2487+
"\n",
2488+
" # Assert the results\n",
2489+
" assert result[\"start_date\"] == \"2024-07-29\"\n",
2490+
" assert result[\"end_date\"] == \"2024-08-05\"\n",
2491+
"\n",
2492+
" # Verify that datetime.now() was called\n",
2493+
" mock_datetime.now.assert_called_once()"
2494+
]
2495+
},
2496+
{
2497+
"cell_type": "markdown",
2498+
"id": "8124b826",
2499+
"metadata": {},
2500+
"source": [
2501+
"This test mocks the `datetime.now()` method to return a fixed date, allowing for predictable and consistent test results."
2502+
]
2503+
},
2504+
{
2505+
"cell_type": "code",
2506+
"execution_count": 53,
2507+
"id": "a6c35293",
2508+
"metadata": {
2509+
"tags": [
2510+
"hide-cell"
2511+
]
2512+
},
2513+
"outputs": [
2514+
{
2515+
"name": "stdout",
2516+
"output_type": "stream",
2517+
"text": [
2518+
"\u001b[1m============================= test session starts ==============================\u001b[0m\n",
2519+
"platform darwin -- Python 3.11.2, pytest-7.4.3, pluggy-1.3.0 -- /Users/khuyentran/.pyenv/versions/3.11.2/bin/python3\n",
2520+
"cachedir: .pytest_cache\n",
2521+
"hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter5/.hypothesis/examples'))\n",
2522+
"rootdir: /Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter5\n",
2523+
"plugins: dvc-3.28.0, hydra-core-1.3.2, typeguard-4.1.5, anyio-4.2.0, hypothesis-6.88.4\n",
2524+
"collected 1 item \u001b[0m\n",
2525+
"\n",
2526+
"test_main.py::test_get_data_for_last_week \u001b[32mPASSED\u001b[0m\n",
2527+
"\n",
2528+
"\u001b[32m============================== \u001b[32m\u001b[1m1 passed\u001b[0m\u001b[32m in 0.09s\u001b[0m\u001b[32m ===============================\u001b[0m\n",
2529+
"\u001b[0m"
2530+
]
2531+
}
2532+
],
2533+
"source": [
2534+
"!pytest -sv test_main.py"
2535+
]
2536+
},
2537+
{
2538+
"cell_type": "markdown",
2539+
"id": "161d68ef-2f73-43ab-98cf-8268bab793a0",
2540+
"metadata": {},
2541+
"source": [
2542+
"2. **Mocking API calls**\n",
2543+
"\n",
2544+
"When testing code that makes external API calls, mocking helps avoid actual network requests during testing.\n",
2545+
"\n",
2546+
"Example: Testing a function that makes an API call"
2547+
]
2548+
},
2549+
{
2550+
"cell_type": "code",
2551+
"execution_count": 35,
2552+
"id": "c0a3fb32",
2553+
"metadata": {},
2554+
"outputs": [
2555+
{
2556+
"name": "stdout",
2557+
"output_type": "stream",
2558+
"text": [
2559+
"Overwriting main.py\n"
2560+
]
2561+
}
2562+
],
2563+
"source": [
2564+
"%%writefile main.py\n",
24132565
"import requests\n",
24142566
"from requests.exceptions import ConnectionError\n",
24152567
"\n",
@@ -2420,36 +2572,91 @@
24202572
" response = requests.get(\"http://localhost:5432\")\n",
24212573
" return response.json()\n",
24222574
" except ConnectionError:\n",
2423-
" return None\n",
2575+
" return None"
2576+
]
2577+
},
2578+
{
2579+
"cell_type": "code",
2580+
"execution_count": 36,
2581+
"id": "2f5b1755",
2582+
"metadata": {},
2583+
"outputs": [
2584+
{
2585+
"name": "stdout",
2586+
"output_type": "stream",
2587+
"text": [
2588+
"Overwriting test_main.py\n"
2589+
]
2590+
}
2591+
],
2592+
"source": [
2593+
"%%writefile test_main.py\n",
2594+
"from unittest.mock import patch\n",
2595+
"from requests.exceptions import ConnectionError\n",
2596+
"from main import get_data\n",
24242597
"\n",
24252598
"\n",
2426-
"def test_get_data_fails():\n",
2599+
"@patch(\"main.requests.get\")\n",
2600+
"def test_get_data_fails(mock_get):\n",
24272601
" \"\"\"Test the get_data function when the API call fails\"\"\"\n",
2428-
" # Mock the requests.get function\n",
2429-
" with patch(\"requests.get\") as mock_get:\n",
2430-
" # Define what happens when the function is called\n",
2431-
" mock_get.side_effect = ConnectionError\n",
2432-
" assert get_data() is None\n",
2602+
" # Define what happens when the function is called\n",
2603+
" mock_get.side_effect = ConnectionError\n",
2604+
" assert get_data() is None\n",
24332605
"\n",
24342606
"\n",
2435-
"def test_get_data_succeeds():\n",
2607+
"@patch(\"main.requests.get\")\n",
2608+
"def test_get_data_succeeds(mock_get):\n",
24362609
" \"\"\"Test the get_data function when the API call succeeds\"\"\"\n",
2437-
" # Mock the requests.get function\n",
2438-
" with patch(\"requests.get\") as mock_get:\n",
2439-
" # Define the return value of the function\n",
2440-
" mock_get.return_value.json.return_value = {\"data\": \"test\"}\n",
2441-
" assert get_data() == {\"data\": \"test\"}\n",
2442-
"\n",
2443-
"```"
2610+
" # Define the return value of the function\n",
2611+
" mock_get.return_value.json.return_value = {\"data\": \"test\"}\n",
2612+
" assert get_data() == {\"data\": \"test\"}"
2613+
]
2614+
},
2615+
{
2616+
"cell_type": "markdown",
2617+
"id": "3a0701b9",
2618+
"metadata": {},
2619+
"source": [
2620+
"These tests mock the `requests.get()` function to simulate both successful and failed API calls, allowing us to test our function's behavior in different scenarios without making actual network requests."
2621+
]
2622+
},
2623+
{
2624+
"cell_type": "code",
2625+
"execution_count": 38,
2626+
"id": "3af021c9",
2627+
"metadata": {
2628+
"tags": [
2629+
"hide-cell"
2630+
]
2631+
},
2632+
"outputs": [
2633+
{
2634+
"name": "stdout",
2635+
"output_type": "stream",
2636+
"text": [
2637+
"\u001b[1m============================= test session starts ==============================\u001b[0m\n",
2638+
"platform darwin -- Python 3.11.2, pytest-7.4.3, pluggy-1.3.0\n",
2639+
"rootdir: /Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter5\n",
2640+
"plugins: dvc-3.28.0, hydra-core-1.3.2, typeguard-4.1.5, anyio-4.2.0, hypothesis-6.88.4\n",
2641+
"collected 2 items \u001b[0m\n",
2642+
"\n",
2643+
"test_main.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m [100%]\u001b[0m\n",
2644+
"\n",
2645+
"\u001b[32m============================== \u001b[32m\u001b[1m2 passed\u001b[0m\u001b[32m in 0.12s\u001b[0m\u001b[32m ===============================\u001b[0m\n",
2646+
"\u001b[0m"
2647+
]
2648+
}
2649+
],
2650+
"source": [
2651+
"!pytest test_main.py"
24442652
]
24452653
},
24462654
{
2447-
"attachments": {},
24482655
"cell_type": "markdown",
2449-
"id": "ff3f0491",
2656+
"id": "e2ffc227-fd00-40ef-aaa2-74e8997aa2cd",
24502657
"metadata": {},
24512658
"source": [
2452-
"[Link to mock](https://docs.python.org/3/library/unittest.mock.html)."
2659+
"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."
24532660
]
24542661
},
24552662
{
@@ -2902,6 +3109,7 @@
29023109
{
29033110
"cell_type": "code",
29043111
"execution_count": 12,
3112+
"id": "e0ca0f88",
29053113
"metadata": {},
29063114
"outputs": [
29073115
{
@@ -4506,7 +4714,7 @@
45064714
"celltoolbar": "Tags",
45074715
"hide_input": false,
45084716
"kernelspec": {
4509-
"display_name": "venv",
4717+
"display_name": "Python 3 (ipykernel)",
45104718
"language": "python",
45114719
"name": "python3"
45124720
},
@@ -4520,7 +4728,7 @@
45204728
"name": "python",
45214729
"nbconvert_exporter": "python",
45224730
"pygments_lexer": "ipython3",
4523-
"version": "3.11.2"
4731+
"version": "3.11.6"
45244732
},
45254733
"toc": {
45264734
"base_numbering": 1,

0 commit comments

Comments
 (0)