2.2. Mục đíchUNIT TEST
1. Unit Test
1.1. Định nghĩa
1.2. Lợi ích khi viết unit test
2. Integration Test
2.1. Định nghĩa
2.2. Mục đích
2.3. Các loại Integration Test
3. Testing trong Python
3.1. Nguyên tắc
3.2. Cơ bản
3.3. Các công cụ
4. Thư viện Unit Test trong Python
4.1. Workflow chuẩn
4.2. Một số định nghĩa quan trọng
4.3. Ví dụ bắt đầu
4.4. Giao diện Command-Line
4.5. Test Discovery
import unittest class FooTest[unittest.TestCase]: """Sample test case""" # preparing to test def setUp[self]: """ Setting up for the test """ print "FooTest:setUp_:begin" ## do something... print "FooTest:setUp_:end" # ending the test def tearDown[self]: """Cleaning up after the test""" print "FooTest:tearDown_:begin" ## do something... print "FooTest:tearDown_:end" # test routine A def testA[self]: """Test routine A""" print "FooTest:testA" # test routine B def testB[self]: """Test routine B""" print "FooTest:testB"
- 4.6. Tổ chức test code
- 4.7. Sử dụng lại test code
- 4.8. Bỏ qua test và dự kiến lỗi
- Phương thức
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
1 vàpython -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
2 cho phép định nghĩa hướng dẫn sẽ được thực hiện trước và sau mỗi phương thức test. python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
3 để chạy test.
4.4. Giao diện Command-Line
Có thể sử dụng giao diện command-line để chạy test từ modules, lớp hoặc từng phương thức test bất kỳ:
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
Các options: -b, -c, -f có thể thấy được khi chạy:
4.5. Test Discovery
Unittest hỗ trợ test discorvery đơn giản - cho phép chạy nhiều test cùng một lúc.
4.6. Tổ chức test code
Như đã nói ở trên, test cases được biểu diễn bởi nittest.TestCase
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
4TestCasepython -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
5FunctionTestCase`.Subclass đơn giản của
TestCase
chỉ là thực thi 1 phương thức test [test_*].import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
Test có thể là số nhiều, được lặp lại. Sử dụng
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
1 để định nghĩa các đối tượng đầu vào sử dụng trong test[kiểu như tạo môi trường cho test]. Ví dụ, ở đây có thể *widget- được sử dụng trong nhiều phương thức test[trong cùng 1 test case] --> cần tối ưu hóa bằng việc thiết lập 1 đối tượng *widget- chung. Nếupython -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
1 raise 1 ngoại lệ khi test đang chạy --> Lỗi, test không được thực hiện.import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
Tương tự, ta có
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
2 để dọn dẹp, xóa bỏ môi trường test. Nếupython -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
1 chạy thành công,python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
2 sẽ được chạy bất kể phương thức test có thành công hay không.Test case instances được nhóm lại với nhau dựa trên feature chúng test.
unittest
cung cấp cơ chế: test suite- -import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
3. Trong phần lớn trường hợp, gọipython -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
3 sẽ thu thập các module test case và thực thi.Muốn tự xây dựng bộ test suite? Có thể sử dụng cách sau:
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite
Và tốt nhất, nên chia test ra module riêng.
4.7. Sử dụng lại test code
4.8. Bỏ qua test và dự kiến lỗi
Unittest cho phép việc bỏ qua phương thức test và có thể cả lớp test. Thêm vào đó, unittest hỗ trợ đánh dấu 1 test chấp nhận lỗi, nếu test đó fail cũng không tính như là 1 failure trong
import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
5.Sử dụng decorator.
DecoratorGiải thích @unittest.skip[reason] Bỏ qua vô điều kiện test, reason nên mô tả lí do bỏ qua. @unittest.skipIf[condition, reason] Bỏ qua test, nếu condition trả về True @unittest.skipUnless[condition, reason] Bỏ qua test trừ khi condition trả về True @unittest.expectedFailure Đánh dấu test là chấp nhận Failure, nếu test fail sẽ không bị tính vào failure
*Các lớp và hàm- _ Here
assertEqual[a, b] | a == b | |
assertNotEqual[a, b] | a != b | |
assertTrue[x] | bool[x] is True | |
assertFalse[x] | bool[x] is False | |
assertIs[a, b] | a is b | 3.1 |
assertIsNot[a, b] | a is not b | 3.1 |
assertIsNone[x] | x is None | 3.1 |
assertIsNotNone[x] | x is not None | 3.1 |
assertIn[a, b] | a in b | 3.1 |
assertNotIn[a, b] | a not in b | 3.1 |
assertIsInstance[a, b] | isinstance[a, b] | 3.2 |
assertNotIsInstance[a, b] | 3.2 |
assertEqual[a, b] | a == b | |
assertNotEqual[a, b] | a != b | 3.1 |
assertTrue[x] | bool[x] is True | 3.2 |
assertFalse[x] | bool[x] is False | 3.2 |
assertIs[a, b] | a is b | 3.4 |
assertEqual[a, b] | a == b | |
assertNotEqual[a, b] | a != b | |
assertTrue[x] | bool[x] is True | 3.1 |
assertFalse[x] | bool[x] is False | 3.1 |
assertIs[a, b] | a is b | 3.1 |
assertIsNot[a, b] | a is not b | 3.1 |
assertIsNone[x] | x is None | 3.1 |
assertIsNotNone[x] | x is not None | 3.2 |
assertIn[a, b] | a in b | 3.2 |
assertEqual[a, b] | a == b | 3.1 |
assertNotEqual[a, b] | a != b | 3.1 |
assertTrue[x] | bool[x] is True | 3.1 |
assertFalse[x] | bool[x] is False | 3.1 |
assertIs[a, b] | a is b | 3.1 |
assertIsNot[a, b] | a is not b | 3.1 |
assertIsNone[x]
x is None
assertIsNotNone[x]
x is not None
assertIn[a, b]
a in b
assertNotIn[a, b]
- a not in b
- Behavior verification: xác định chính xác những phương thức sẽ được SUT gọi đến trong collaborators, việc xác minh sẽ không phải là xác minh trạng thái kết thúc là chính xác, mà sẽ là trình tự các bước được thực hiện chính xác.
5.1.3. Sự khác nhau giữa Mocks và Stubs
4 kiểu Test Double:
- *Dummy- object được pass, tuy nhiên không bao giờ được dùng thực sự. Thông thường chỉ được sử dụng để điền vào danh sách tham số.
- *Stubs- cung cấp câu trả lời được "đóng lại" để gọi trong lúc test, thường không phản hồi lại bất cứ thứ gì ngoài những thứ đã được lập trình sẵn trong test. Stubs cũng có thể ghi lại bản ghi thông tin về những lần gọi đến, chẳng hạn như 1 email gateway stub có thể ghi nhớ lại msgs mà nó gửi đi, hoặc số msgs nó gửi đi. Nói cách khác, stub biểu diễn 1 tập các phương thức interfaces cho test subject[tập các phương thức tương tự có thể thấy trong subject thực sự?]. Khi test subject gọi đến phương thức stub, stub sẽ phản hồi lại với tập các kết quả đã định trước. Nó có thể sinh ra lỗi hoặc ngoại lệ, điều này cũng đã được định trước. Stub có thể theo dõi sự tương tác của nó với test subject, tuy nhiên chỉ trong phạm vi chương trình test.
- *Fake- object thực sự được sử dụng, tuy nhiên thường có 1 số shortcut khiến nó không thích hợp cho việc sản xuất ???[memory database]. Fake biểu diễn 1 tập các phương thức interfaces, theo dõi sự tương tác với test subject. Không giống stub, fake thực xử lý dữ liệu đầu vào từ test subject, và đưa ra được kết quả dựa trên dữ liệu đó. In short, a fake is a functional, but non-production version of the actual test resource. It lacks the checks and balances found in resource? Sử dụng thuật toán đơn giản, hiếm khi hoặc không bao giờ lưu trữ và dịch chuyển dữ liệu. --> Với fake và stub, có thể test xem test subject gọi đúng phương thức với đầu vào đúng. Có thẻ test làm thế nào subject xử lý kết quả và lỗi/ngoại lệ. -->
import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
6. --> Nếu muốn biết nếu test subject gọi đến cùng 1 phương thức 2 lần, hoặc phương thức được thực hiện đúng thứ tự? -->import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
7In short, a fake is a functional, but non-production version of the actual test resource. It lacks the checks and balances found in resource? Sử dụng thuật toán đơn giản, hiếm khi hoặc không bao giờ lưu trữ và dịch chuyển dữ liệu. --> Với fake và stub, có thể test xem test subject gọi đúng phương thức với đầu vào đúng. Có thẻ test làm thế nào subject xử lý kết quả và lỗi/ngoại lệ. -->import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
6. --> Nếu muốn biết nếu test subject gọi đến cùng 1 phương thức 2 lần, hoặc phương thức được thực hiện đúng thứ tự? -->import unittest class DefaultWidgetSizeTestCase[unittest.TestCase]: def test_default_widget_size[self]: widget = Widget['The widget'] self.assertEqual[widget.size[], [50, 50]]
7 - *Mock- : đối tượng được xác định những kì vọng cần đạt được.
With fakes and stubs, you can test if the test subject called the right method with the right input. You can test how the subject handles the result and how it reacts to an error or exception. These tests are known as state verification. But what if you want to know if the test subject called the same method twice? What if you want to know if it called several methods in the proper order? Such tests are known as behavior verification, and to do them, you need mocks.
5.1.4. Classical and Mockist Testing
import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
3 sử dụng đối tượng thực khi nào có thể, và đối tượng đúp [double] khi việc sử dụng đối tượng thực gặp vấn đề.import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
4 lúc nào cũng sử dụng mock cho bất kỳ đối tượng nào.
5.2. Mock trong Python
Note:
import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']5 có trong bộ thư viện chuẩn từ Python3.3. Còn từ 3.3 đổ về 2.7, có trong thư viện `unittest.mock
Sử dụng Decorator, tuy nhiên phải chú ý đến thứ tự, theo chiều ngược lại. Ví dụ:
@mock.patch['mymodule.sys'] @mock.patch['mymodule.os'] @mock.patch['mymodule.os.path'] def test_something[self, mock_os_path, mock_os, mock_sys]: pass
Hoặc có thể sử dụng
import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
6 để tạo ra 1 instance cho lớp được cung cấp.Hoặc mock.Mock, mock.MagicMock. Khi cần đưa ra sự lựa chọn giữa
import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
7,import unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
8 vàimport unittest class SimpleWidgetTestCase[unittest.TestCase]: def setUp[self]: self.widget = Widget['The widget'] def test_default_widget_size[self]: self.assertEqual[self.widget.size[], [50,50], 'incorrect default size'] def test_widget_resize[self]: self.widget.resize[100,150] self.assertEqual[self.widget.size[], [100,150], 'wrong size after resize']
6, luôn ưu tiên sử dụng auto-spec.
5.3. Mock class
5.4. Cấu trúc cơ bản của Mock class
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite0 class[green] có 2 lớp cha
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite1 và
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite2.
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite1 xác định ra routine cần thiết bằng các mock object. Nó sẽ ghi đè 1 số magic methods, cho chúng behavior mặc định? Và nó cũng cung cấp assert routines nhằm theo dõi, lần theo behavior của mock. Đối với
def suite[]: suite = unittest.TestSuite[] suite.addTest[WidgetTestCase['test_default_size']] suite.addTest[WidgetTestCase['test_resize']] return suite2, nó cập nhật các magic methods giúp cho mock object có thể gọi được.
6. Tài liệu tham khảo
- Unit test và Function test - Blog Duyet Dev
- Unit test và kinh nghiệm viết unit test
- Testing your code
- Unit test python
- Mock Aren't Stubs
- Improve Your Python: Understanding Unit Testing
- Introduction to Mocking in Python
- Using Mocks in Python
- Python Mocking 101: Fake It Before You Make It