Hướng dẫn python-unittest github - python-github đơn nhất

UNIT TEST

  • UNIT 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
      • 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
    • 5. Thư viện Mock trong Python
      • 5.1. Mocks aren't stubs
        • 5.1.1. Regular Tests
        • 5.1.2. Test với Mock objects
        • 5.1.3. Sự khác nhau giữa Mocks và Stubs
        • 5.1.4. Classical and Mockist Testing
      • 5.2. Mock trong Python
      • 5.3. Mock class
      • 5.4. Cấu trúc cơ bản của Mock class
    • 6. Tài liệu tham khảo

1. Unit Test

1.1. Định nghĩa

  • Unit Test - Kiểm thử mức đơn vị
  • Unit - Thành phần phần mềm nhỏ nhất có thể kiểm tra được - hàm, thủ tục, lớp và phương thức.
  • "Thời gian tốn cho Unit Test sẽ được đền bù bằng việc tiết kiệm rất nhiều thời gian và chi phí cho việc kiểm tra và sửa lỗi ở các mức kiểm tra sau đó"
  • Thường do LTV thực hiện.
  • Mục đích: bảo đảm thông tin được xử lý và xuất là chính xác, trong mối tương quan với dữ liệu nhập và chức năng của Unit. Điều đó đòi hỏi tất cả các nhánh bên trong Unit đều phải kiểm tra để phát hiện nhánh phát sinh lỗi.

1.2. Lợi ích khi viết unit test

  • 2. Integration Test

2. Integration Test

2.1. Định nghĩa

2.2. Mục đích

2.2. Mục đích

  • 2.3. Các loại Integration Test
  • 3. Testing trong Python

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ụ

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

3.2. Cơ bản

  • 3.3. Các công cụ
  • 4. Thư viện Unit Test trong Python

3.3. Các công cụ

  • Py.test.
  • Nose.
  • Tox.
  • Unittest2.
  • Mock.

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.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
  • 4.6. Tổ chức test code

Hướng dẫn python-unittest github - python-github đơn nhất

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
    4TestCase
      python -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ếu
      python -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ếu
      python -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ọi
      python -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

MethodChecks thatNew in
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
MethodChecks thatNew in
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
MethodChecks thatNew in
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
MethodChecks thatNew in
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 suite
0 class(green) có 2 lớp cha
  def suite():
      suite = unittest.TestSuite()
      suite.addTest(WidgetTestCase('test_default_size'))
      suite.addTest(WidgetTestCase('test_resize'))
      return suite
1 và
  def suite():
      suite = unittest.TestSuite()
      suite.addTest(WidgetTestCase('test_default_size'))
      suite.addTest(WidgetTestCase('test_resize'))
      return suite
2.
  def suite():
      suite = unittest.TestSuite()
      suite.addTest(WidgetTestCase('test_default_size'))
      suite.addTest(WidgetTestCase('test_resize'))
      return suite
1 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 suite
2, 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