Skip to content

Commit 8c6de4c

Browse files
test: ex_3_2 to ex_3_5 covered and finished ch3.
1 parent 362c8c3 commit 8c6de4c

File tree

5 files changed

+196
-31
lines changed

5 files changed

+196
-31
lines changed

docs/3. Handling CPU and IO Bound Tasks/ex_3_2.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@
33
import httpx
44

55

6-
async def main():
7-
t = time.time()
6+
async def fetch_data(url):
87
async with httpx.AsyncClient() as client:
9-
task1 = asyncio.create_task(client.get('https://www.example.com/'))
10-
task2 = asyncio.create_task(client.get('https://www.example.com/'))
11-
task3 = asyncio.create_task(client.get('https://www.example.com/'))
8+
task1 = asyncio.create_task(client.get(url))
9+
task2 = asyncio.create_task(client.get(url))
10+
task3 = asyncio.create_task(client.get(url))
1211
response1 = await task1
1312
response2 = await task2
1413
response3 = await task3
15-
print(f'response1: {response1}, response2: {response2}, response3: {response3}')
14+
return response1, response2, response3
15+
16+
17+
async def main():
18+
t = time.time()
19+
response1, response2, response3 = await fetch_data('https://www.example.com/')
20+
print(f'response1: {response1}, response2: {response2}, response3: {response3}')
1621
print(f'It took {time.time() - t} s')
1722

1823
asyncio.run(main())

docs/3. Handling CPU and IO Bound Tasks/ex_3_3.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
import httpx
44

55

6-
async def main():
7-
t = time.time()
8-
url = 'https://www.example.com/'
6+
async def fetch_data(url):
97
async with httpx.AsyncClient() as client:
108
tasks = [client.get(url) for _ in range(3)]
119
obj = await asyncio.gather(*tasks)
12-
print(f'obj: {obj}, obj type: {type(obj)}')
10+
return obj
11+
12+
13+
async def main():
14+
t = time.time()
15+
obj = await fetch_data('https://www.example.com/')
16+
print(f'obj: {obj}, obj type: {type(obj)}')
1317
print(f'It took {time.time() - t} s')
1418

1519
asyncio.run(main())

docs/3. Handling CPU and IO Bound Tasks/ex_3_4.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@
22
import httpx
33

44

5-
url = 'https://www.example.org/'
6-
t = time.time()
7-
response1 = httpx.get(url)
8-
response2 = httpx.get(url)
9-
response3 = httpx.get(url)
10-
print(f'It took {time.time() - t} s')
11-
print(f'response1: {response1}, response2: {response2}, response3: {response3}')
5+
def fetch_data(url):
6+
response1 = httpx.get(url)
7+
response2 = httpx.get(url)
8+
response3 = httpx.get(url)
9+
return response1, response2, response3
10+
11+
12+
def main():
13+
url = 'https://www.example.org/'
14+
t = time.time()
15+
response1, response2, response3 = fetch_data(url)
16+
print(f'It took {time.time() - t} s')
17+
print(f'response1: {response1}, response2: {response2}, response3: {response3}')
18+
19+
20+
if __name__ == '__main__':
21+
main()

docs/3. Handling CPU and IO Bound Tasks/ex_3_5.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,42 @@
33
from functools import partial
44

55

6-
def cpu_bound_task(a: int, n: int) -> float:
6+
def cpu_bound_task(a: int, n: int) -> int | float:
77
_sum = 0
88
for number in range(n):
99
_sum += a
1010
return _sum
1111

1212

13-
t = time.time()
14-
value = cpu_bound_task(2, 100000000)
15-
print(f'value: {value}')
16-
value = cpu_bound_task(2, 100000000)
17-
print(f'It took without multiprocessing {time.time() - t} s')
18-
print(f'value: {value}')
13+
def without_multiprocessing(step: int, value: int) -> tuple[int, int, float]:
14+
t = time.time()
15+
value1 = cpu_bound_task(step, value)
16+
value2 = cpu_bound_task(step, value)
17+
time_taken = time.time() - t
1918

20-
cpu_bound_partial = partial(cpu_bound_task, 2)
19+
return value1, value2, time_taken
2120

22-
with Pool(2) as p:
23-
t = time.time()
24-
value = p.map(cpu_bound_partial, [100000000, 100000000])
25-
print(f'It took with multiprocessing {time.time() - t} s')
26-
print(f'value: {value}')
21+
22+
def with_multiprocessing(step: int, value: int) -> tuple((int, int, float)):
23+
cpu_bound_partial = partial(cpu_bound_task, step)
24+
25+
with Pool(2) as p:
26+
t = time.time()
27+
values = p.map(cpu_bound_partial, [value, value])
28+
time_taken = time.time() - t
29+
30+
return *values, time_taken
31+
32+
33+
def main() -> None:
34+
value1, value2, time_taken_without_mp = without_multiprocessing(step=2, value=100000000)
35+
print(f'Without multiprocessing, value1: {value1}, value2: {value2}')
36+
print(f'time taken: {time_taken_without_mp} s')
37+
print('======================================')
38+
value1, value2, time_taken_with_mp = with_multiprocessing(step=2, value=100000000)
39+
print(f'With multiprocessing, value1: {value1}, value2: {value2}')
40+
print(f'time taken: {time_taken_with_mp} s')
41+
42+
43+
if __name__ == '__main__':
44+
main()

tests/test_ch3.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
import time
44
import asyncio
55
import unittest
6-
from unittest.mock import patch, AsyncMock
6+
from unittest.mock import patch, AsyncMock, MagicMock
77

88
sys.path.append(os.path.abspath('docs/3. Handling CPU and IO Bound Tasks'))
99
from ex_3_1 import fetch_data as fetch_data_ex_1, main as main_ex_1
10+
from ex_3_2 import fetch_data as fetch_data_ex_2, main as main_ex_2
11+
from ex_3_3 import fetch_data as fetch_data_ex_3, main as main_ex_3
12+
from ex_3_4 import fetch_data as fetch_data_ex_4, main as main_ex_4
13+
from ex_3_5 import cpu_bound_task, without_multiprocessing, with_multiprocessing, main as main_ex_5
1014

1115

1216
class TestEx1AsyncHttpx(unittest.IsolatedAsyncioTestCase):
@@ -47,5 +51,129 @@ async def test_main_success(self, mock_fetch):
4751
mocked_print.assert_any_call(f"response is: {mock_fetch.return_value}")
4852

4953

54+
class TestEx2AsyncHttpx(unittest.IsolatedAsyncioTestCase):
55+
@patch('httpx.AsyncClient.get')
56+
async def test_fetch_data_success(self, mock_get):
57+
"""Test successful fetching of data."""
58+
mock_response = mock_get.return_value
59+
mock_response.status_code = 200
60+
mock_response.text = 'Success'
61+
62+
url = 'https://www.example.com/'
63+
response1, response2, response3 = await fetch_data_ex_2(url)
64+
mock_get.assert_called_with(url)
65+
assert mock_get.call_count == 3
66+
67+
self.assertEqual(response1.status_code, 200)
68+
self.assertEqual(response2.status_code, 200)
69+
self.assertEqual(response3.status_code, 200)
70+
self.assertEqual(response1.text, 'Success')
71+
self.assertEqual(response2.text, 'Success')
72+
self.assertEqual(response3.text, 'Success')
73+
74+
@patch('ex_3_2.fetch_data', new_callable=AsyncMock)
75+
async def test_main_success(self, mock_fetch):
76+
"""Test main function when fetch_data is successful."""
77+
mock_fetch.return_value.status_code = 200
78+
mock_fetch.return_value = '<Response [200 OK]>', '<Response [200 OK]>', '<Response [200 OK]>'
79+
80+
with patch('builtins.print') as mocked_print:
81+
await main_ex_2()
82+
mocked_print.assert_any_call(f"response1: {mock_fetch.return_value[0]}, "
83+
f"response2: {mock_fetch.return_value[1]}, "
84+
f"response3: {mock_fetch.return_value[2]}")
85+
86+
87+
class TestEx3AsyncHttpx(unittest.IsolatedAsyncioTestCase):
88+
@patch('httpx.AsyncClient.get')
89+
async def test_fetch_data_success(self, mock_get):
90+
"""Test successful fetching of data."""
91+
mock_response = mock_get.return_value
92+
mock_response.status_code = 200
93+
mock_response.text = 'Success'
94+
95+
url = 'https://www.example.com/'
96+
response1, response2, response3 = await fetch_data_ex_3(url)
97+
mock_get.assert_called_with(url)
98+
assert mock_get.call_count == 3
99+
100+
self.assertEqual(response1.status_code, 200)
101+
self.assertEqual(response2.status_code, 200)
102+
self.assertEqual(response3.status_code, 200)
103+
self.assertEqual(response1.text, 'Success')
104+
self.assertEqual(response2.text, 'Success')
105+
self.assertEqual(response3.text, 'Success')
106+
107+
@patch('ex_3_3.fetch_data', new_callable=AsyncMock)
108+
async def test_main_success(self, mock_fetch):
109+
"""Test main function when fetch_data is successful."""
110+
mock_fetch.return_value.status_code = 200
111+
mock_fetch.return_value = ['<Response [200 OK]>', '<Response [200 OK]>', '<Response [200 OK]>']
112+
113+
with patch('builtins.print') as mocked_print:
114+
await main_ex_3()
115+
mocked_print.assert_any_call(f'obj: {mock_fetch.return_value}, obj type: {type(mock_fetch.return_value)}')
116+
117+
118+
class TestEx4AsyncHttpx(unittest.TestCase):
119+
@patch('httpx.get')
120+
def test_fetch_data_success(self, mock_get):
121+
"""Test successful fetching of data."""
122+
mock_response = mock_get.return_value
123+
mock_response.status_code = 200
124+
mock_response.text = 'Success'
125+
126+
url = 'https://www.example.com/'
127+
response1, response2, response3 = fetch_data_ex_4(url)
128+
mock_get.assert_called_with(url)
129+
assert mock_get.call_count == 3
130+
131+
self.assertEqual(response1.status_code, 200)
132+
self.assertEqual(response2.status_code, 200)
133+
self.assertEqual(response3.status_code, 200)
134+
self.assertEqual(response1.text, 'Success')
135+
self.assertEqual(response2.text, 'Success')
136+
self.assertEqual(response3.text, 'Success')
137+
138+
@patch('ex_3_4.fetch_data', new_callable=MagicMock)
139+
def test_main_success(self, mock_fetch):
140+
"""Test main function when fetch_data is successful."""
141+
mock_fetch.return_value.status_code = 200
142+
mock_fetch.return_value = '<Response [200 OK]>', '<Response [200 OK]>', '<Response [200 OK]>'
143+
144+
with patch('builtins.print') as mocked_print:
145+
main_ex_4()
146+
mocked_print.assert_any_call(f"response1: {mock_fetch.return_value[0]}, "
147+
f"response2: {mock_fetch.return_value[1]}, "
148+
f"response3: {mock_fetch.return_value[2]}")
149+
150+
151+
class TestMultiprocessingPerformance(unittest.TestCase):
152+
153+
def setUp(self):
154+
self.step = 2
155+
self.value = 100000000
156+
157+
def test_with_and_without_mp(self):
158+
value1, value2, time_taken = without_multiprocessing(self.step, self.value)
159+
self.assertEqual(value1, value2)
160+
self.assertEqual(value1, self.value * self.step)
161+
162+
value1_mp, value2_mp, time_taken_mp = with_multiprocessing(self.step, self.value)
163+
self.assertEqual(value1_mp, value2_mp)
164+
self.assertEqual(value1_mp, self.value * self.step)
165+
self.assertEqual(value2_mp, self.value * self.step)
166+
self.assertLessEqual(time_taken_mp * 1.7, time_taken,
167+
msg='time taken without mp is less than 1.7 times of with mp!')
168+
169+
def test_main(self):
170+
with patch('builtins.print') as mocked_print:
171+
main_ex_5()
172+
value = self.value * self.step
173+
mocked_print.assert_any_call(f'Without multiprocessing, value1: {value}, value2: {value}')
174+
mocked_print.assert_any_call(f'======================================')
175+
mocked_print.assert_any_call(f'With multiprocessing, value1: {value}, value2: {value}')
176+
177+
50178
if __name__ == '__main__':
51179
unittest.main()

0 commit comments

Comments
 (0)