Cách sử dụng đơn nhất để viết trường hợp thử nghiệm cho một hàm trong Python
Các thư viện chuẩn của Python bao gồm cácunittest
module để giúp bạn viết và chạy thử nghiệm cho mã Python của bạn. Kiểm tra viết bằng unittest
module có thể giúp bạn tìm thấy lỗi trong các chương trình của bạn, và ngăn chặn hồi quy từ xảy ra khi bạn thay đổi mã của bạn theo thời gian. Đội tôn trọng phát triển thử nghiệm điều khiển có thể tìm thấy unittest
hữu ích đảm bảo tất cả các mã có tác giả có một bộ tương ứng của bài kiểm tra.
Trong hướng dẫn này, bạn sẽ sử dụng Python unittest
module để viết một bài kiểm tra cho một hàm.
Yêu cầu
Để tận dụng tối đa hướng dẫn này, bạn cần :
- Hiểu biết về các hàm trong Python. Bạn có thể xem lại hướng dẫn Cách xác định hàm trong Python 3 , là một phần của loạt bài Cách viết mã trong Python 3 .
Định nghĩa một lớp con TestCase
Một trong những lớp học quan trọng nhất được cung cấp bởi các unittest
module được đặt tên TestCase
. TestCase
cung cấp giàn giáo chung để kiểm tra các chức năng của ta . Hãy xem xét một ví dụ:
import unittest def add_fish_to_aquarium(fish_list): if len(fish_list) > 10: raise ValueError("A maximum of 10 fish can be added to the aquarium") return {"tank_a": fish_list} class TestAddFishToAquarium(unittest.TestCase): def test_add_fish_to_aquarium_success(self): actual = add_fish_to_aquarium(fish_list=["shark", "tuna"]) expected = {"tank_a": ["shark", "tuna"]} self.assertEqual(actual, expected)
Đầu tiên ta nhập khẩu unittest
để làm cho các module có sẵn để mã của ta . Sau đó, ta xác định chức năng mà ta muốn kiểm tra — đây là add_fish_to_aquarium
.
Trong trường hợp này, hàm add_fish_to_aquarium
của ta chấp nhận một danh sách các loài cá có tên là fish_list
và gây ra lỗi nếu fish_list
có nhiều hơn 10 phần tử. Các chức năng sau đó trả về một ánh xạ từ điển tên của một bể cá "tank_a"
đến cho fish_list
.
Một lớp có tên TestAddFishToAquarium
được định nghĩa là một lớp con của unittest.TestCase
. Phương thức có tên test_add_fish_to_aquarium_success
được xác định trên TestAddFishToAquarium
. test_add_fish_to_aquarium_success
gọi hàm add_fish_to_aquarium
với một đầu vào cụ thể và xác minh giá trị trả về thực tế trùng với giá trị mà ta mong đợi được trả về.
Bây giờ ta đã xác định một lớp con TestCase
với một bài kiểm tra, hãy xem lại cách ta có thể thực hiện bài kiểm tra đó.
Thực thi TestCase
Trong phần trước, ta đã tạo một lớp con TestCase
có tên là TestAddFishToAquarium
. Từ cùng một folder với file test_add_fish_to_aquarium.py
, hãy chạy kiểm tra đó bằng lệnh sau:
- python -m unittest test_add_fish_to_aquarium.py
Ta đã gọi module thư viện Python có tên là unittest
với python -m unittest
. Sau đó, ta cung cấp đường dẫn đến file chứa TestAddFishToAquarium
TestCase
của ta làm đối số.
Sau khi ta chạy lệnh này, ta nhận được kết quả như sau:
Output. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
Các unittest
module chạy thử nghiệm của ta và nói với ta rằng ta thử nghiệm chạy OK
. Độc thân .
trên dòng đầu tiên của kết quả đại diện cho bài kiểm tra đã vượt qua của ta .
Lưu ý: TestCase
công nhận các phương pháp thử nghiệm là bất kỳ phương pháp nào bắt đầu với test
. Ví dụ: def test_add_fish_to_aquarium_success(self)
được công nhận là một bài kiểm tra và sẽ được chạy như vậy. Ngược lại, def example_test(self)
sẽ không được công nhận là một bài kiểm tra vì nó không bắt đầu bằng test
. Chỉ các phương thức bắt đầu bằng test
sẽ được chạy và được báo cáo khi bạn chạy python -m unittest ...
Bây giờ ta hãy thử một bài kiểm tra với một thất bại.
Ta sửa đổi dòng được đánh dấu sau trong phương pháp thử nghiệm của bạn để đưa ra lỗi:
import unittest def add_fish_to_aquarium(fish_list): if len(fish_list) > 10: raise ValueError("A maximum of 10 fish can be added to the aquarium") return {"tank_a": fish_list} class TestAddFishToAquarium(unittest.TestCase): def test_add_fish_to_aquarium_success(self): actual = add_fish_to_aquarium(fish_list=["shark", "tuna"]) expected = {"tank_a": ["rabbit"]} self.assertEqual(actual, expected)
Kiểm tra đã sửa đổi sẽ không thành công vì add_fish_to_aquarium
sẽ không trả về "rabbit"
trong danh sách cá thuộc "tank_a"
. Hãy chạy thử nghiệm.
, từ cùng một folder với test_add_fish_to_aquarium.py
ta chạy:
- python -m unittest test_add_fish_to_aquarium.py
Khi ta chạy lệnh này, ta nhận được kết quả như sau:
OutputF ====================================================================== FAIL: test_add_fish_to_aquarium_success (test_add_fish_to_aquarium.TestAddFishToAquarium) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_add_fish_to_aquarium.py", line 13, in test_add_fish_to_aquarium_success self.assertEqual(actual, expected) AssertionError: {'tank_a': ['shark', 'tuna']} != {'tank_a': ['rabbit']} - {'tank_a': ['shark', 'tuna']} + {'tank_a': ['rabbit']} ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (failures=1)
Đầu ra lỗi cho biết thử nghiệm của ta không thành công. Sản lượng thực tế của {'tank_a': ['shark', 'tuna']}
không phù hợp với kỳ vọng (không chính xác) mà ta đã thêm vào test_add_fish_to_aquarium.py
của: {'tank_a': ['rabbit']}
. Cũng lưu ý thay vì a .
, dòng đầu tiên của kết quả bây giờ có F
Trong khi .
nhân vật được outputted khi kiểm tra vượt qua, F
là sản phẩm khi unittest
điều hành một thử nghiệm thất bại.
Bây giờ ta đã viết và chạy một bài kiểm tra, hãy thử viết một bài kiểm tra khác cho một hành vi khác của hàm add_fish_to_aquarium
.
Kiểm tra một chức năng phát sinh một ngoại lệ
unittest
cũng có thể giúp ta xác minh hàm add_fish_to_aquarium
tạo ra một Ngoại lệ ValueError
nếu đưa quá nhiều cá làm đầu vào. Hãy mở rộng ví dụ trước đó của ta và thêm một phương pháp thử nghiệm mới có tên test_add_fish_to_aquarium_exception
:
import unittest def add_fish_to_aquarium(fish_list): if len(fish_list) > 10: raise ValueError("A maximum of 10 fish can be added to the aquarium") return {"tank_a": fish_list} class TestAddFishToAquarium(unittest.TestCase): def test_add_fish_to_aquarium_success(self): actual = add_fish_to_aquarium(fish_list=["shark", "tuna"]) expected = {"tank_a": ["shark", "tuna"]} self.assertEqual(actual, expected) def test_add_fish_to_aquarium_exception(self): too_many_fish = ["shark"] * 25 with self.assertRaises(ValueError) as exception_context: add_fish_to_aquarium(fish_list=too_many_fish) self.assertEqual( str(exception_context.exception), "A maximum of 10 fish can be added to the aquarium" )
Phương thức thử nghiệm mới test_add_fish_to_aquarium_exception
cũng gọi hàm add_fish_to_aquarium
, nhưng nó thực hiện như vậy với một danh sách dài 25 phần tử có chứa chuỗi "shark"
lặp lại 25 lần.
test_add_fish_to_aquarium_exception
sử dụng trình quản lý ngữ cảnh with self.assertRaises(...)
do TestCase
cung cấp để kiểm tra xem add_fish_to_aquarium
từ chối danh sách đã nhập quá lâu hay không. Đối số đầu tiên của self.assertRaises
là lớp Exception mà ta mong đợi sẽ được nâng lên — trong trường hợp này là ValueError
. Trình quản lý ngữ cảnh self.assertRaises
được liên kết với một biến có tên là exception_context
. Thuộc tính exception
trên exception_context
chứa ValueError
cơ bản mà add_fish_to_aquarium
đã nêu ra. Khi ta gọi str()
trên ValueError
đó để truy xuất thông báo của nó, nó sẽ trả về thông báo ngoại lệ chính xác mà ta mong đợi.
Từ cùng một folder với test_add_fish_to_aquarium.py
, hãy chạy thử nghiệm của ta :
- python -m unittest test_add_fish_to_aquarium.py
Khi ta chạy lệnh này, ta nhận được kết quả như sau:
Output.. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Đáng chú ý, thử nghiệm của ta sẽ không thành công nếu add_fish_to_aquarium
không tăng một Ngoại lệ hoặc tăng một Ngoại lệ khác (ví dụ: TypeError
thay vì ValueError
).
Lưu ý: unittest.TestCase
cho thấy một số phương pháp khác ngoài assertEqual
và assertRaises
mà bạn có thể sử dụng. Danh sách đầy đủ các phương pháp xác nhận có thể được tìm thấy trong tài liệu , nhưng một lựa chọn được bao gồm ở đây:
phương pháp | Quả quyết |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(a) | bool(a) is True |
assertFalse(a) | bool(a) is False |
assertIsNone(a) | a is None |
assertIsNotNone(a) | a is not None |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
Bây giờ ta đã viết một số thử nghiệm cơ bản, hãy xem cách ta có thể sử dụng các công cụ khác do TestCase
cung cấp để khai thác bất kỳ mã nào ta đang thử nghiệm.
Sử dụng phương pháp setUp
để tạo tài nguyên
TestCase
cũng hỗ trợ phương thức setUp
để giúp bạn tạo tài nguyên trên cơ sở mỗi lần kiểm tra. Phương thức setUp
có thể hữu ích khi bạn có một bộ mã chuẩn bị chung mà bạn muốn chạy trước mỗi và mọi thử nghiệm của bạn . setUp
cho phép bạn đặt tất cả mã chuẩn bị này vào một nơi duy nhất, thay vì lặp đi lặp lại nó cho mỗi bài kiểm tra riêng lẻ.
Hãy xem một ví dụ:
import unittest class FishTank: def __init__(self): self.has_water = False def fill_with_water(self): self.has_water = True class TestFishTank(unittest.TestCase): def setUp(self): self.fish_tank = FishTank() def test_fish_tank_empty_by_default(self): self.assertFalse(self.fish_tank.has_water) def test_fish_tank_can_be_filled(self): self.fish_tank.fill_with_water() self.assertTrue(self.fish_tank.has_water)
test_fish_tank.py
định nghĩa một lớp có tên FishTank
. Ban đầu FishTank.has_water
được đặt thành False
, nhưng có thể được đặt thành True
bằng cách gọi FishTank.fill_with_water()
. Các TestCase
lớp con TestFishTank
định nghĩa một phương thức có tên setUp
mà instantiates một mới FishTank
dụ và chuyển nhượng mà dụ để self.fish_tank
.
Vì setUp
được chạy trước mọi phương pháp thử nghiệm riêng lẻ, một version FishTank
mới được khởi tạo cho cả test_fish_tank_empty_by_default
và test_fish_tank_can_be_filled
. test_fish_tank_empty_by_default
xác minh has_water
bắt đầu là False
. test_fish_tank_can_be_filled
xác minh has_water
được đặt thành True
sau khi gọi fill_with_water()
.
Từ cùng một folder với test_fish_tank.py
, ta có thể chạy:
- python -m unittest test_fish_tank.py
Nếu ta chạy lệnh trước đó, ta sẽ nhận được kết quả sau:
Output.. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Kết quả cuối cùng cho thấy cả hai bài kiểm tra đều vượt qua.
setUp
cho phép ta viết mã chuẩn bị chạy cho tất cả các bài kiểm tra của ta trong một lớp con TestCase
.
Lưu ý: Nếu bạn có nhiều file thử nghiệm với các lớp con TestCase
mà bạn muốn chạy, hãy xem xét sử dụng python -m unittest discover
để chạy nhiều hơn một file thử nghiệm. Chạy python -m unittest discover --help
trợ giúp để biết thêm thông tin.
Sử dụng phương pháp tearDown
để làm sạch tài nguyên
TestCase
hỗ trợ một bản sao cho phương thức setUp
có tên là tearDown
. tearDown
hữu ích nếu, chẳng hạn, ta cần xóa các kết nối đến database hoặc các sửa đổi được thực hiện đối với hệ thống file sau mỗi lần kiểm tra hoàn tất. Ta sẽ xem xét một ví dụ sử dụng tearDown
với hệ thống file :
import os import unittest class AdvancedFishTank: def __init__(self): self.fish_tank_file_name = "fish_tank.txt" default_contents = "shark, tuna" with open(self.fish_tank_file_name, "w") as f: f.write(default_contents) def empty_tank(self): os.remove(self.fish_tank_file_name) class TestAdvancedFishTank(unittest.TestCase): def setUp(self): self.fish_tank = AdvancedFishTank() def tearDown(self): self.fish_tank.empty_tank() def test_fish_tank_writes_file(self): with open(self.fish_tank.fish_tank_file_name) as f: contents = f.read() self.assertEqual(contents, "shark, tuna")
test_advanced_fish_tank.py
định nghĩa một lớp có tên AdvancedFishTank
. AdvancedFishTank
tạo một file có tên fish_tank.txt
và ghi chuỗi "shark, tuna"
vào file đó. AdvancedFishTank
cũng cho thấy một phương thức empty_tank
loại bỏ file fish_tank.txt
. Các TestAdvancedFishTank
TestCase
lớp con định nghĩa cả một setUp
và tearDown
phương pháp.
Các setUp
phương pháp tạo ra một AdvancedFishTank
dụ và gán nó vào self.fish_tank
. Các tearDown
phương pháp gọi các empty_tank
phương pháp trên self.fish_tank
: Đảm bảo này mà fish_tank.txt
file được lấy ra sau mỗi phương pháp thử nghiệm chạy. Bằng cách này, mỗi bài kiểm tra bắt đầu với một phương tiện chặn sạch. Phương thức test_fish_tank_writes_file
xác minh nội dung mặc định của "shark, tuna"
được ghi vào file fish_tank.txt
.
Từ cùng một folder với test_advanced_fish_tank.py
hãy chạy:
- python -m unittest test_advanced_fish_tank.py
Ta sẽ nhận được kết quả sau:
Output. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
tearDown
cho phép bạn viết mã dọn dẹp được chạy cho tất cả các bài kiểm tra của bạn trong một lớp con TestCase
.
Kết luận
Trong hướng dẫn này, bạn đã viết TestCase
lớp học với khẳng định khác nhau, sử dụng các setUp
và tearDown
phương pháp, và chạy thử nghiệm của bạn từ dòng lệnh.
Các unittest
module cho thấy các lớp học bổ sung và tiện ích mà bạn không bao gồm trong hướng dẫn này. Đến đây bạn có một đường cơ sở, bạn có thể sử dụng các unittest
tài liệu module của để tìm hiểu thêm về các lớp học khác có sẵn và các tiện ích. Bạn cũng có thể quan tâm đến Cách thêm kiểm thử đơn vị vào dự án Django của bạn .
Các tin liên quan
Cách chuyển đổi int thành float trong Python 32020-09-18
Bắt đầu với Python - Get Request
2020-09-15
Cách sử dụng f-string để tạo chuỗi trong Python 3
2020-09-10
Cách chuyển đổi số nguyên thành chuỗi trong Python 3
2020-09-03
Cách chuyển đổi chuỗi thành số nguyên trong Python 3
2020-09-03
Cách sử dụng module bộ sưu tập trong Python 3
2020-08-19
Cách chuyển đổi kiểu dữ liệu trong Python 3
2020-08-07
Cách sử dụng hàm bản đồ Python
2020-08-03
Cách sử dụng quy trình con để chạy các chương trình bên ngoài trong Python 3
2020-07-30
Làm thế nào để đánh lừa một mạng neural trong Python 3
2020-07-30