2010-06-24 18 views
5

Khung kiểm thử của tôi hiện dựa trên tiện ích thử nghiệm mà bản thân nó bắt nguồn từ python pydev Eclipse Á hậu thử nghiệm. Tôi đang chuyển sang sử dụng Mũi, có nhiều tính năng của thử nghiệm tùy chỉnh của tôi nhưng có vẻ là mã chất lượng tốt hơn.Có thể thực hiện các phép kiểm tra chỉ chạy trên mũi mà là các lớp con của TestCase hoặc TestSuite (như unittest.main())

Bộ thử nghiệm của tôi bao gồm một số lớp thử nghiệm trừu tượng mà trước đây không bao giờ chạy. Testrunner python chuẩn (và tùy chỉnh của tôi) chỉ chạy các cá thể của unittest.TestCase và unittest.TestSuite.

Tôi nhận thấy rằng kể từ khi tôi chuyển sang Mũi, mọi thứ bắt đầu bằng tên "kiểm tra" gây khó chịu ... bởi vì quy ước đặt tên chúng tôi sử dụng cho các hỗn hợp thử nghiệm cũng giống như một lớp thử nghiệm Mũi. Trước đây chúng chưa bao giờ chạy thử nghiệm vì chúng không phải là trường hợp của TestCase hoặc TestSuite. Rõ ràng là tôi có thể đặt tên lại các phương pháp để loại trừ từ "kiểm tra" khỏi tên của chúng ... sẽ mất một lúc vì khung kiểm tra rất lớn và có nhiều thừa kế. Mặt khác nó sẽ gọn gàng nếu có một cách để làm cho Nose chỉ nhìn thấy TestCases và TestSuites như là runnable ... và không có gì khác.

Việc này có thể thực hiện được không?

Trả lời

5

Bạn có thể thử chơi với tùy chọn -m cho nosetests. Từ tài liệu:

Một lớp học thử nghiệm là một lớp được định nghĩa trong một module kiểm tra phù hợp với testMatch hoặc là một lớp con của unittest.TestCase

-m bộ mà testMatch, bằng cách này bạn có thể vô hiệu hóa thử nghiệm mọi thứ bắt đầu bằng kiểm tra.

Một điều nữa là bạn có thể thêm __test__ = False vào khai báo lớp học thử nghiệm của mình để đánh dấu "không phải là bài kiểm tra".

+0

Lệnh '__test__ = false' Bí quyết là không phải là rất hữu ích trong trường hợp của một lớp cha mẹ, điều này sẽ buộc trẻ em lớp để xác định một cách rõ ràng '__test__ = TRUE' hoặc nó sẽ bị bỏ qua, đó là một nguy hiểm lừa để sử dụng. – Guibod

0

Bạn có thể sử dụng đối số của mũi --attr để chỉ định thuộc tính được đặt bởi unittest.TestCase. Ví dụ, tôi sử dụng:

nosetests --attr="assertAlmostEqual" 

Bạn có thể nhận được thậm chí cẩn thận hơn bằng cách sử dụng hoặc khớp:

nosetests -A "assertAlmostEqual or addTest" 

Xem unittest's documentation cho một danh sách đầy đủ các phương pháp/thuộc tính, và mũi của description of the capabilities of the --attr plugin.

2

Nếu bạn muốn một lớp thử nghiệm thực sự trừu tượng, bạn chỉ có thể kế thừa lớp trừu tượng từ đối tượng, sau đó kế thừa trong testcases sau.

Ví dụ:

class AbstractTestcases(object): 
    def test_my_function_does_something(self): 
     self.assertEquals("bar", self.func()) 

Và sau đó sử dụng nó với:

class TestMyFooFunc(AbstractTestcases, unittest.TestCase): 
    def setUp(self): 
     self.func = lambda: "foo" 

Sau đó nosetests sẽ nhận chỉ testcases trong TestMyFooFunc và không những trong AbstractTestCases.

+0

Đó là một cách tốt đẹp để tránh vấn đề, nhưng thử nghiệm trừu tượng sau đó là khá khó đọc trong IDE của tôi. – Guibod

0

Một phụ lục để trả lời @nailxx 's:

Bạn có thể thiết lập __test__ = False trong lớp cha mẹ và sau đó sử dụng một metaclass (xem This question với một số giải thích rực rỡ) để thiết lập nó lại là True khi subclassing.

(Cuối cùng, tôi tìm thấy một cái cớ để sử dụng một metaclass!)

Mặc dù __test__ là một thuộc tính gạch dưới đôi, chúng ta phải thiết lập một cách rõ ràng để True, vì không phải cài đặt nó sẽ gây ra python chỉ để tra cứu thuộc tính tiếp tục lên trên MRO và đánh giá nó thành False.

Do đó, chúng tôi cần kiểm tra tại buổi thuyết trình lớp cho dù một trong các lớp cha có __test__ = False hay không. Nếu trường hợp này xảy ra và định nghĩa lớp hiện tại chưa tự đặt __test__, chúng tôi sẽ thêm '__test__': True vào dict thuộc tính.

Mã kết quả trông như thế này:

class TestWhenSubclassedMeta(type): 
    """Metaclass that sets `__test__` back to `True` when subclassed. 

    Usage: 

     >>> class GenericTestCase(TestCase, metaclass=TestWhenSubclassed): 
     ...  __test__ = False 
     ... 
     ...  def test_something(self): 
     ...   self.fail("This test is executed in a subclass, only.") 
     ... 
     ... 
     >>> class SpecificTestCase(GenericTestCase): 
     ...  pass 

    """ 

    def __new__(mcs, name, bases, attrs): 
     ATTR_NAME = '__test__' 
     VALUE_TO_RESET = False 
     RESET_VALUE = True 

     values = [getattr(base, ATTR_NAME) for base in bases 
        if hasattr(base, ATTR_NAME)] 

     # only reset if the first attribute is `VALUE_TO_RESET` 
     try: 
      first_value = values[0] 
     except IndexError: 
      pass 
     else: 
      if first_value == VALUE_TO_RESET and ATTR_NAME not in attrs: 
       attrs[ATTR_NAME] = RESET_VALUE 

     return super().__new__(mcs, name, bases, attrs) 

Người ta có thể mở rộng này để một số hành vi tiềm ẩn hơn như “nếu tên bắt đầu bằng Abstract, thiết __test__ = False tự động”, nhưng tôi cho bản thân mình sẽ giữ sự phân công rõ ràng cho rõ ràng.


Hãy để tôi dán đơn giản để giải thích hành vi - và nhắc nhở mọi người nên dành hai phút để kiểm tra mã của họ sau khi giới thiệu một tính năng.

from unittest import TestCase 

from .base import TestWhenSubclassedMeta 


class SubclassesTestCase(TestCase): 
    def test_subclass_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = False 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertIn('__test__', C.__dict__) 

    def test_subclass_not_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = True 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertNotIn('__test__', C.__dict__) 

    def test_subclass_attr_not_set(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      pass 

     class C(Base): 
      pass 

     with self.assertRaises(AttributeError): 
      getattr(C, '__test__') 
0

Bạn cũng có thể sử dụng nhiều thừa kế trên cấp trường hợp kiểm tra và để lớp cơ sở kế thừa chỉ từ object. Xem this thread:

class MyBase(object): 
    def finishsetup(self): 
     self.z=self.x+self.y 

    def test1(self): 
     self.assertEqual(self.z, self.x+self.y) 

    def test2(self): 
     self.assert_(self.x > self.y) 

class RealCase1(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=10 
     self.y=5 
     MyBase.finishsetup(self) 

class RealCase2(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=42 
     self.y=13 
     MyBase.finishsetup(self)