Thay vì TDD [Phát triển theo hướng kiểm tra] buộc bạn phải nghĩ về các bài kiểm tra trước, người ta có thể thực hành TPD [Phát triển song song kiểm tra]. Trong TPD, bạn bắt đầu với việc phát triển mã, nhưng trong quá trình phát triển, bạn kiểm tra tính chính xác của mã bằng cách viết các bài kiểm tra và thực thi chúng [thay vì chạy mã trực tiếp hoặc sử dụng bảng điều khiển]
Giới thiệu. Trong bài viết này, chúng tôi đang thảo luận về bản vá. trăn đối tượng. Hầu hết thời gian, phần mềm chúng tôi viết tương tác trực tiếp với cái mà chúng tôi gọi là dịch vụ "bẩn". Đặt các dịch vụ quan trọng đối với ứng dụng nhưng tương tác của chúng có các tác dụng phụ có chủ ý nhưng không mong muốn, tôi. e. , các dịch vụ không mong muốn trong bối cảnh thực hiện kiểm tra tự động
Ví dụ: bạn đang xây dựng ứng dụng xã hội và muốn thử nghiệm tính năng đăng bài mới trên Facebook, nhưng bạn muốn tránh đăng bài lên Facebook mỗi khi chạy bộ thử nghiệm. Thư viện unittest Python chứa một gói con có tên là unittest. giả [hoặc giả nếu bạn khai báo nó là một phụ thuộc]. Điều này cung cấp một phương tiện mạnh mẽ và thuận tiện để chế giễu và che giấu những tác dụng phụ không mong muốn này
Việc kiểm tra là rất quan trọng để đảm bảo rằng logic phần mềm của bạn là chính xác, đáng tin cậy và xanh khi viết mã mạnh mẽ. Tuy nhiên, lệ phí của bài kiểm tra phụ thuộc vào mức độ bài kiểm tra thể hiện các tiêu chí đó tốt như thế nào. Các rào cản, logic phức tạp và các phụ thuộc không thể đoán trước khiến việc viết các bài kiểm tra thực tế trở nên khó khăn. Thư viện mục mô phỏng Python unittest. giả cho phép vượt qua những ranh giới này
Cách chế giễu hoặc vá lỗi có nghĩa là gì?
Một đối tượng giả thay thế và bắt chước đối tượng thực trong môi trường thử nghiệm. Nó là một thiết bị linh hoạt và mạnh mẽ để nâng cao chất lượng bài kiểm tra của bạn
Một mục đích của việc sử dụng các mục mô phỏng Python là thao túng hành vi mã của bạn trong quá trình kiểm tra
Ví dụ: nếu mã của bạn yêu cầu HTTP đến một nhà cung cấp dịch vụ bên ngoài, thử nghiệm sẽ chạy tốt nhất có thể dự đoán được với số lượng nhà cung cấp dịch vụ hoạt động như dự đoán. Những thay đổi nhanh chóng trong hành vi của các dịch vụ bên ngoài này cũng có thể gây ra các thảm họa không liên tục trong bộ kiểm tra của bạn
Do đó, chúng tôi khuyên bạn nên xem mã của mình trong môi trường được kiểm soát. Thay đổi các yêu cầu thực bằng các đối tượng giả giúp bạn mô phỏng thảm họa của nhà cung cấp bên ngoài và các phản hồi thành công theo cách có thể dự đoán được
Có thể khó kiểm tra các vùng nhất định trong cơ sở mã của bạn. Những vùng đó bao gồm các khối "bên cạnh" và câu lệnh "nếu" khó thực thi. Các đối tượng mô phỏng trong Python có thể giúp bạn quản lý đường dẫn thực thi mã của mình để tiếp cận các khu vực đó và cải thiện mức độ phù hợp của mã
Bản vá lỗi là gì?
đơn vị nhất. mock cung cấp một cơ chế mô phỏng đối tượng mạnh mẽ được gọi là patch[]. Điều này tìm kiếm một đối tượng trong một mô-đun cụ thể và thay thế đối tượng đó bằng một mô hình. Patch[] thường được sử dụng làm công cụ trang trí hoặc trình quản lý bối cảnh để cung cấp một vùng để mô phỏng đối tượng đích
Chức năng patch[] được sử dụng như một trình phát hiện như thế nào?
Nếu bạn muốn giả lập một đối tượng thông qua chức năng kiểm tra của mình, hãy sử dụng patch[] làm công cụ trang trí chức năng. Để xem cách nó hoạt động, hãy sắp xếp lại my_calendar. báo cáo py bằng cách đặt logic và kiểm tra trong các tài liệu riêng biệt
Ví dụ. Bây giờ, chúng tôi đưa ra một ví dụ về hàm patch[] được sử dụng như một trình phát hiện. Ví dụ được đưa ra dưới đây -
Các chức năng này hiện có trong tệp của chúng, tách biệt với các bài kiểm tra. Sau đó xây dựng lại các bài kiểm tra trong một tệp có tên tests. py. Cho đến nay, chúng tôi đã vá khỉ vào đối tượng trong tệp nơi đối tượng sống. Bản vá khỉ thay thế một đối tượng bằng một đối tượng khác trong thời gian chạy
Chức năng patch[] được sử dụng như một trình quản lý ngữ cảnh như thế nào?
Đôi khi, bạn sẽ muốn sử dụng patch[] làm trình quản lý ngữ cảnh thay vì trình trang trí. Một số lý do khiến bạn có thể chọn trình quản lý bối cảnh bao gồm những điều sau
- Bạn chỉ cần giả lập một mục cho một phần của phạm vi thử nghiệm
- Bạn đã sử dụng quá nhiều trình trang trí hoặc tham số, điều này làm ảnh hưởng đến sự rõ ràng của bài kiểm tra của bạn
Phần kết luận. Bạn đã học được nhiều về cách chế nhạo các đối tượng với unittest. chế nhạo
bây giờ bạn có thể. sử dụng các mô hình giả để mô phỏng các đối tượng trong séc của bạn, nghiên cứu thông tin sử dụng và cá nhân hóa các giá trị trả về cũng như các hệ quả phụ của các đối tượng mô phỏng để hiểu cách các tiện ích được sử dụng
khám phá các đối tượng patch[] trong quá trình codebase của bạn
tránh xa những rắc rối khi sử dụng các đối tượng giả Python
Tạo ra một nền tảng bí quyết để giúp viết các bài kiểm tra cao hơn. Mocks cung cấp thông tin chi tiết về mã của bạn mà trong bất kỳ trường hợp nào khác, bạn có thể không nhận được nữa
Một tuyên bố từ chối trách nhiệm cuối cùng. Hãy cẩn thận để sử dụng các đối tượng giả một cách tiết kiệm. Thật dễ dàng để sử dụng chức năng đối tượng giả của Python để tạo các bản giả làm giảm giá trị các bài kiểm tra của bạn
[khỉ-] vá lỗi là một kỹ thuật để thay đổi hành vi mã mà không làm thay đổi nguồn của nó. Nó được thực hiện trong thời gian chạy, thường bằng cách ghi đè các thuộc tính của các đối tượng hiện có. Một đối tượng có thể là một thể hiện của một số loại, một lớp hoặc thậm chí là một mô-đun. Kỹ thuật này được [ab] sử dụng phổ biến nhất cho các bài kiểm tra khi chúng ta không thể vượt qua các bản mô phỏng một cách đơn giản
# by default, it will create a new instance of Mock and put it in place of on_commit
@patch["django.db.transaction.on_commit"]
def test_booking_flight__successful_boking__schedueles_notification_after_commit[on_commit_mock]:
code_that_imports_on_commit_from_djang_db_etc[]
on_commit_mock.assert_called_once_with[...]
Một ví dụ ấn tượng khác là thư viện gevent biến mã đồng bộ thành không đồng bộ bằng cách sử dụng bản vá khỉ
# the first thing to do is to call monkey.patch_all[]
# to turn code into asynchronous one
from gevent import monkey; monkey.patch_all[]
import time
import requests
from gevent.pywsgi import WSGIServer
def application[env, start_response]:
start = time.time[]
# we use requests [which is not asyncio-friendly!]
# to check how much time it takes example.com to respond
response = requests.get['//example.com/']
end = time.time[]
if not response.ok:
start_response['500 Internal Server Errord', [['Content-Type', 'text/html']]]
return [b'Something is wrong with the site!']
start_response['200 OK', [['Content-Type', 'text/html']]]
return [f"Server took {end - start}s to respond".encode[]]
if __name__ == '__main__':
WSGIServer[['127.0.0.1', 8080], application].serve_forever[]
Hãy đánh giá nó bằng cách sử dụng wrk
wrk -c 20 -t 5 -d 5s --timeout 5s //localhost:8080
# requests to example.com takes on average in ~0.25s
# without gevent.patch_all
Running 5s test @ //localhost:8080
5 threads and 20 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.90s 1.59s 4.83s 47.37%
Req/Sec 2.11 1.52 4.00 57.89%
19 requests in 5.06s, 2.79KB read
Requests/sec: 3.76
Transfer/sec: 565.48B
# with gevent.patch_all
Running 5s test @ //localhost:8080
5 threads and 20 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 364.20ms 116.89ms 613.86ms 74.90%
Req/Sec 11.72 6.85 30.00 59.35%
259 requests in 5.10s, 38.00KB read
Requests/sec: 50.77 -------- compared to 3.76 without gevent...
Transfer/sec: 7.45KB
Gevent đã yêu cầu một thư viện thân thiện với coroutine và nhờ đồng thời, nó cho phép máy chủ mẫu của chúng tôi xử lý hơn 13. Yêu cầu gấp 5 lần mỗi giây
Cuối cùng, chúng tôi có một chương trình có đồng thời dựa trên coroutine [nguyên tắc giống như trong asyncio hoặc nút. js] nhưng mã của nó vẫn giống như mã đồng bộ. Chúng tôi không cần các thư viện hợp tác, đặc biệt hoặc các từ khóa không đồng bộ/đang chờ trong mã của chúng tôi. Nó gần giống như ma thuật
Vá lỗi trong các bài kiểm tra
Python bao gồm một tiện ích để vá lỗi, tôi. e. đơn vị nhất. chế nhạo. vá. Cách sử dụng mặc định là trang trí chức năng kiểm tra của chúng tôi. Giả sử chúng ta có chế độ xem Django trông như thế này…
from api.client import ApiClient
def get_stats[request]:
site_url = request.GET.get['url']
api_client = ApiClient[]
stats = api_client.get_stats_for[site_url]
seconds = int[stats['time_to_respond'].total_seconds[]]
return JsonResponse[{'latency': f'{seconds}s'}]
và chúng tôi muốn kiểm tra nó. Chúng tôi nhận thấy nó có một phụ thuộc – ApiClient từ một mô-đun khác. Nếu chúng tôi muốn kiểm tra chế độ xem get_stats theo cách đáng tin cậy, có thể dự đoán được, chúng tôi cần sử dụng test-double thay vì ApiClient. Tuy nhiên, không có cách nào đơn giản để làm như vậy. Nếu nó được chuyển đến get_stats làm đối số, thì chúng ta chỉ cần chuyển Mock thay thế
def get_stats[request, api_client_class]:
...
api_client = api_client_class[]
...
…nhưng không phải vậy. Tuy nhiên, chúng ta vẫn có thể sử dụng trình trang trí bản vá
@patch['api.client.ApiClient.get_stats_for']
def test__get_stats__time_to_respond_is_timedelta__formats_as_seconds[client]:
response = client.get['/stats/']
assert response.json[] == {'latency': '63s'}
Điều này vẫn chưa kết thúc, nhưng nếu chúng tôi đưa trình gỡ lỗi vào thử nghiệm, chúng tôi sẽ nhận thấy rằng ApiClient. get_stats_for hiện là MagicMock
@patch['api.client.ApiClient.get_stats_for']
def test__get_stats__time_to_respond_is_timedelta__formats_as_seconds[client]:
breakpoint[]
response = client.get['/stats/']
assert response.json[] == {'latency': '63s'}
# after running pytest...
[Pdb] from api.client import ApiClient
[Pdb] ApiClient.get_stats_for
Nó có nghĩa là chế giễu của chúng tôi đã thành công. Chúng tôi đã thay thế một phụ thuộc có vấn đề bằng một Mock. Nhân tiện, nếu bạn đang tìm kiếm các phương pháp hay nhất để sử dụng mô phỏng, hãy xem hướng dẫn chính thức [gần như] của tôi về mô phỏng trong Python hoặc tại sao mô phỏng có thể nguy hiểm khi bị lạm dụng
Bây giờ, thử nghiệm vẫn không thành công vì get_stats nhận được MagicMock trong khi nó mong đợi một từ điển. Chúng ta cần tham số hóa mô hình. Chúng ta có thể làm như vậy bằng cách chuyển đối số thứ hai tới @patch
@patch[
'api.client.ApiClient.get_stats_for',
Mock[return_value={'time_to_respond': timedelta[minutes=1, seconds=3]}]
]
def test__get_stats__time_to_respond_is_timedelta__formats_as_seconds[client]:
response = client.get['/stats/']
assert response.json[] == {'latency': '63s'}
Điều này về cơ bản có nghĩa là thay vì api. khách hàng. ApiClient. get_stats_vì chúng tôi muốn một Mô phỏng mà khi được gọi, sẽ trả về {‘time_to_Response’. múi giờ[phút=1, giây=3]}
Vá mà không cần trang trí
bản vá cũng có thể được sử dụng như một trình quản lý ngữ cảnh. Kết quả trả về sẽ là một Mô hình được chèn vào vị trí của một thuộc tính đang được vá
________số 8“Bản vá Python không hoạt động. ” – làm thế nào cho đúng?
Đôi khi bạn sẽ phải đối mặt với tình huống mặc dù có sự hiện diện của trình trang trí bản vá hoặc trình quản lý bối cảnh, phần phụ thuộc sẽ trông như thể nó không được vá chút nào. Nói tóm lại, có thể là do có nhiều tài liệu tham khảo hiện có về thứ bạn đang cố gắng vá. Mã đang kiểm tra sử dụng một mã, nhưng bạn đã vá thành công một mã khác. Ca mổ thành công nhưng bệnh nhân tử vong. phải làm gì?
Nói tóm lại, bạn cần đảm bảo rằng bạn vá cùng một tham chiếu mà mã đang thử nghiệm sử dụng
# views/__init__.py
from api import client
def get_stats[site_url]:
api_client = client.ApiClient[]
stats = api_client.get_stats_for[site_url]
seconds = int[stats['time_to_respond'].total_seconds[]]
return {'latency': f'{seconds}s'}
# test_view.py
@patch[
# we patch class in the module where it is imported into
# [the same our code under test comes from]
'view.client.ApiClient.get_stats_for',
...
]
def test__get_stats__time_to_respond_is_timedelta__formats_as_seconds[]:
...
Xem phần Nơi vá lỗi của unittest. tài liệu giả để biết thêm chi tiết. Ngoài ra, bạn có thể sử dụng một giải pháp thay thế tiện lợi cho bản vá, đó là bản vá. vật
vá. đối tượng - đơn giản hơn để làm cho đúng
vá. đối tượng rất đơn giản để sử dụng – bạn chỉ cần nhập đối tượng có thuộc tính mà bạn muốn vá và áp dụng bản vá. vật
# the first thing to do is to call monkey.patch_all[]
# to turn code into asynchronous one
from gevent import monkey; monkey.patch_all[]
import time
import requests
from gevent.pywsgi import WSGIServer
def application[env, start_response]:
start = time.time[]
# we use requests [which is not asyncio-friendly!]
# to check how much time it takes example.com to respond
response = requests.get['//example.com/']
end = time.time[]
if not response.ok:
start_response['500 Internal Server Errord', [['Content-Type', 'text/html']]]
return [b'Something is wrong with the site!']
start_response['200 OK', [['Content-Type', 'text/html']]]
return [f"Server took {end - start}s to respond".encode[]]
if __name__ == '__main__':
WSGIServer[['127.0.0.1', 8080], application].serve_forever[]
0Nếu bạn muốn sử dụng bản vá. đối tượng cho một phương thức, bạn nhập một lớp. Nếu bạn muốn vá. phản đối một hàm hoặc toàn bộ lớp, hãy nhập mô-đun mà chúng nằm trong đó
Bạn có nên vá?
[khỉ-] vá nên được sử dụng một cách tiết kiệm. Đó nên là phương sách cuối cùng của bạn. Trong mã của tôi, tôi không có lựa chọn nào khác ngoài việc vá nhờ tiêm phụ thuộc
Về lâu dài, giá cho những mánh khóe như vậy là rất, rất cao. Vá thường có nghĩa là chạm và thay đổi các chi tiết triển khai theo cách mà các tác giả không lường trước được. Điều này giới thiệu khớp nối bổ sung với những thứ không nên có. Có nghĩa là họ sẽ khó thay đổi hơn