Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Sử dụng Mock trong Unittest của Python
Không phải lúc nào mình cũng có thể kiểm tra mã nguồn một cách trực tiếp, đặc biệt khi mã nguồn phụ thuộc vào các yếu tố bên ngoài như hệ thống, mạng, hoặc các I/O operation. Để giải quyết vấn đề này, Python cung cấp mô-đun unittest.mock
với lớp Mock
, giúp mình mô phỏng các đối tượng thực tế và kiểm tra mã nguồn một cách độc lập và hiệu quả hơn. Trong bài viết này, mình sẽ tìm hiểu về lớp Mock
của unittest.mock
, cách sử dụng nó để mô phỏng các đối tượng khác, và lợi ích của việc sử dụng Mock
trong việc kiểm tra mã nguồn Python.
Giới thiệu về lớp Mock của unittest trong Python
Mocks mô phỏng hành vi của các đối tượng thực tế. Để kiểm tra một đối tượng phụ thuộc vào các đối tượng khác một cách độc lập, bạn sử dụng các đối tượng mock để mô phỏng các đối tượng thực.
Để mô phỏng các đối tượng, bạn sử dụng mô-đun unittest.mock
. Mô-đun này cung cấp lớp Mock
cho phép bạn mô phỏng các đối tượng khác. Nó cũng cung cấp lớp MagicMock
, là một lớp con của Mock
. Ngoài các phương thức và thuộc tính của lớp Mock
, lớp MagicMock
còn có các triển khai của tất cả các phương thức đặc biệt như __str__
và __repr__
.
Xem ví dụ sau:
Bài viết này được đăng tại [free tuts .net]
from unittest.mock import Mock # tạo một đối tượng mock mới mock = Mock() # mô phỏng hàm api mock.api.return_value = { 'id': 1, 'message': 'hello' } # gọi hàm api print(mock.api())
Kết quả:
{'id': 1, 'message': 'hello'}
Cách hoạt động:
Đầu tiên, import lớp Mock
từ mô-đun unittest.mock
:
from unittest.mock import Mock
Thứ hai, tạo một thể hiện mới của lớp Mock
:
mock = Mock()
Thứ ba, mô phỏng hàm api()
và gán giá trị trả về của nó là một từ điển:
mock.api.return_value = { 'id': 1, 'message': 'hello' }
Cuối cùng, gọi hàm api()
từ đối tượng mock. Nó sẽ trả về giá trị đã gán:
print(mock.api())
Trong ví dụ này, mình có hai đối tượng mock: mock
và mock.api
.
Thêm câu lệnh print()
vào chương trình để xem cách nó hoạt động:
from unittest.mock import Mock # tạo một đối tượng mock mới mock = Mock() print(mock) # mô phỏng hàm api mock.api.return_value = { 'id': 1, 'message': 'hello' } print(mock.api) # gọi hàm api print(mock.api())
Kết quả:
<Mock id='1830094470496'> <Mock name='mock.api' id='1830100086416'> {'id': 1, 'message': 'hello'}
Kết quả cho thấy hai đối tượng Mock.
Tóm lại, nếu bạn gán một thuộc tính không tồn tại trên đối tượng Mock, Python sẽ trả về một đối tượng mock mới. Nhờ vào tính năng này, bạn có thể sử dụng lớp Mock
để mô phỏng bất kỳ đối tượng nào bạn muốn.
Khi nào sử dụng mock của unittest trong Python
Đây là những trường hợp bạn nên xem xét sử dụng mock:
- Các lệnh gọi hệ thống
- Mạng
- Hoạt động I/O
- Đồng hồ & thời gian, múi giờ
- Hoặc các trường hợp khác mà kết quả không thể dự đoán trước
Tại sao sử dụng mock của unittest trong Python
Lợi ích của mock bao gồm:
- Tăng tốc độ kiểm tra
- Loại trừ các yếu tố dư thừa bên ngoài
- Làm cho các kết quả không thể đoán trước trở nên có thể đoán trước
Ví dụ về sử dụng Mock trong Python Unittest
Giả sử bạn có một mô-đun gọi là odometer.py
:
from random import randint def speed(): return randint(40, 120) def alert(): s = speed() if s < 60 or s > 100: return True return False
Trong mô-đun sensor.py
:
- Hàm
speed()
trả về tốc độ hiện tại của một phương tiện. Nó trả về một giá trị ngẫu nhiên giữa 40 và 120. Trong thế giới thực, hàm này sẽ đọc dữ liệu từ đồng hồ đo tốc độ. - Hàm
alert()
trả vềTrue
nếu tốc độ hiện tại thấp hơn 60 km/h và cao hơn 100 km/h. Hàmalert()
sử dụng hàmspeed()
để lấy tốc độ hiện tại.
Sẽ rất khó kiểm tra hàm alert()
vì giá trị trả về bởi hàm speed()
là khác nhau. Để giải quyết, bạn có thể sử dụng lớp Mock
.
Sau đây là cách tạo mô-đun kiểm tra test_odometer.py
để kiểm tra hàm alert()
:
import unittest from unittest.mock import Mock import odometer class TestOdometer(unittest.TestCase): def test_alert_normal(self): odometer.speed = Mock() odometer.speed.return_value = 70 self.assertFalse(odometer.alert()) def test_alert_overspeed(self): odometer.speed = Mock() odometer.speed.return_value = 100 self.assertFalse(odometer.alert()) def test_alert_underspeed(self): odometer.speed = Mock() odometer.speed.return_value = 59 self.assertTrue(odometer.alert())
Chạy kiểm tra:
python -m unittest test_odometer.py -v
Kết quả:
test_alert_normal (test_odometer.TestOdometer) ... ok test_alert_overspeed (test_odometer.TestOdometer) ... ok test_alert_underspeed (test_odometer.TestOdometer) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
Kết bài
Việc sử dụng lớp Mock
của mô-đun unittest.mock
trong Python giúp bạn dễ dàng mô phỏng các đối tượng khác để kiểm tra mã nguồn một cách độc lập và hiệu quả. Nhờ vào các tính năng mạnh mẽ của lớp Mock
, bạn có thể loại bỏ các yếu tố không thể đoán trước và tăng tốc độ kiểm tra. Hy vọng rằng qua bài viết này, bạn đã hiểu rõ hơn về cách sử dụng mock để nâng cao chất lượng và độ tin cậy của mã nguồn Python.