बेस और सब क्लास के साथ पायथन यूनिट टेस्ट


148

वर्तमान में मेरे पास कुछ यूनिट परीक्षण हैं जो परीक्षणों का एक सामान्य सेट साझा करते हैं। यहाँ एक उदाहरण है:

import unittest

class BaseTest(unittest.TestCase):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

उपरोक्त का आउटपुट है:

Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s

OK

क्या उपरोक्त को फिर से लिखने का कोई तरीका है ताकि पहले testCommonको बुलाया न जाए?

EDIT: उपर्युक्त 5 परीक्षण चलाने के बजाय, मैं चाहता हूं कि यह केवल 4 परीक्षण चलाए, 2 SubTest1 से और दूसरा 2 SubTest2 से। ऐसा लगता है कि पायथन यूनिटेस्ट अपने दम पर मूल बेसटेस्ट चला रहा है और मुझे ऐसा होने से रोकने के लिए एक तंत्र की आवश्यकता है।


मुझे लगता है कि किसी ने इसका उल्लेख नहीं किया है, लेकिन क्या आपके पास मुख्य भाग को बदलने और एक परीक्षण सूट चलाने का विकल्प है जिसमें बेसस्टेस्ट के सभी उपवर्ग हैं?
कोन साइक

जवाबों:


154

मल्टीपल इनहेरिटेंस का उपयोग करें, इसलिए आम टेस्ट के साथ आपकी क्लास खुद को टेस्टकेस से विरासत में नहीं लेती है।

import unittest

class CommonTests(object):
    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(unittest.TestCase, CommonTests):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(unittest.TestCase, CommonTests):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

1
यह अब तक का सबसे सुरुचिपूर्ण समाधान है।
थिएरी लाम

27
यह विधि केवल सेटअप और टियरडाउन विधियों के लिए काम करती है यदि आप आधार कक्षाओं के क्रम को उलट देते हैं। क्योंकि विधियाँ unittest.TestCase में परिभाषित की गई हैं, और वे सुपर को कॉल नहीं करते हैं (), तो कॉमनटैस्ट में किसी भी सेटअप और आंसू विधियों को MRO में पहले होना चाहिए, या उन्हें बिल्कुल भी नहीं बुलाया जाएगा।
इयान क्लेलैंड

32
बस इयान क्लेलैंड की टिप्पणी को स्पष्ट करने के लिए ताकि यह मेरे जैसे लोगों के लिए स्पष्ट हो जाए: यदि आप जोड़ते हैं setUpऔर कक्षा में tearDownविधियां करते हैं CommonTests, और आप चाहते हैं कि उन्हें व्युत्पन्न कक्षाओं में प्रत्येक परीक्षा के लिए बुलाया जाए, तो आपको आधार कक्षाओं के क्रम को उल्टा करना होगा, ताकि यह हो जाएगा class SubTest1(CommonTests, unittest.TestCase):।
डेनिस गोलोमेज़ोव

6
मैं वास्तव में इस दृष्टिकोण का प्रशंसक नहीं हूं। यह उस कोड में एक अनुबंध स्थापित करता है जिसे कक्षाएं दोनों से विरासत में लेनी चाहिए unittest.TestCase और CommonTests । मुझे लगता है कि setUpClassनीचे दी गई विधि सर्वश्रेष्ठ है और मानव त्रुटि के लिए कम संभावना है। या तो एक कंटेनर क्लास में बेसटेस्ट क्लास को लपेटना या लपेटना जो थोड़ा अधिक हैक है, लेकिन टेस्ट रन प्रिंटआउट में स्किप संदेश से बचा जाता है।
डेविड सैंडर्स

10
इस एक के साथ समस्या है pylint एक फिट है क्योंकि CommonTestsउस तरीके को लागू करना है जो उस वर्ग में मौजूद नहीं है।
मैडिसिनिस्ट

145

एकाधिक वंशानुक्रम का उपयोग न करें, यह आपको बाद में काटेगा ।

इसके बजाय आप बस अपने आधार वर्ग को अलग मॉड्यूल में स्थानांतरित कर सकते हैं या इसे रिक्त वर्ग के साथ लपेट सकते हैं:

class BaseTestCases:

    class BaseTest(unittest.TestCase):

        def testCommon(self):
            print('Calling BaseTest:testCommon')
            value = 5
            self.assertEqual(value, 5)


class SubTest1(BaseTestCases.BaseTest):

    def testSub1(self):
        print('Calling SubTest1:testSub1')
        sub = 3
        self.assertEqual(sub, 3)


class SubTest2(BaseTestCases.BaseTest):

    def testSub2(self):
        print('Calling SubTest2:testSub2')
        sub = 4
        self.assertEqual(sub, 4)

if __name__ == '__main__':
    unittest.main()

उत्पादन:

Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

6
यह मेरा पसंदीदा है। यह कम से कम हैकी साधन है और ओवरराइडिंग विधियों में हस्तक्षेप नहीं करता है, एमआरओ में बदलाव नहीं करता है और मुझे बेस क्लास में सेटअप, सेटअप क्लैस आदि को परिभाषित करने की अनुमति देता है।
हनीस

6
मैं गंभीरता से नहीं मिलता है (जादू कहाँ से आता है?), लेकिन यह मेरे अनुसार सबसे अच्छा समाधान है :) जावा से आ रहा है, मुझे कई विरासत से नफरत है ...
एडुआर्ड बर्थे

4
@Edouardb यूनीटेस्ट केवल मॉड्यूल-स्तरीय कक्षाएं चलाता है जो टेस्टकेस से विरासत में मिला है। लेकिन बेसटेस्ट मॉड्यूल-स्तर नहीं है।
जोश

एक बहुत ही समान विकल्प के रूप में, आप एबीसी को नो-आर्ग्स फ़ंक्शन के अंदर परिभाषित कर सकते हैं जो एबीसी को लौटाता है जिसे कहा जाता है
उत्तराखंड

34

आप इस समस्या को एकल आदेश से हल कर सकते हैं:

del(BaseTest)

तो कोड इस तरह दिखेगा:

import unittest

class BaseTest(unittest.TestCase):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

del(BaseTest)

if __name__ == '__main__':
    unittest.main()

3
बेसटेस्ट मॉड्यूल का एक सदस्य है, जबकि इसे परिभाषित किया जा रहा है, इसलिए यह सबटेस्ट्स के बेस क्लास के रूप में उपयोग के लिए उपलब्ध है। परिभाषा पूरी होने से ठीक पहले, डेल () इसे एक सदस्य के रूप में हटा देता है, इसलिए जब यह मॉड्यूल में टेस्टकैस उपवर्गों की खोज करता है, तो सबसे उपयुक्त रूपरेखा उसे नहीं मिलेगी।
मुहस्तिथ

3
यह एक भयानक जवाब है! मैं इसे @MatthewMarshall की तुलना में अधिक पसंद करता हूं क्योंकि उसके समाधान में, आपको पाइलिंट से वाक्यविन्यास त्रुटियां मिलेंगी, क्योंकि self.assert*विधियां एक मानक वस्तु में मौजूद नहीं हैं।
सिंपलीकॉनाएजेस

1
यदि BaseTest आधार वर्ग या उसके उपवर्गों, जैसे में और कहीं भी संदर्भित है जब सुपर () विधि ओवरराइड की बुला काम नहीं करता है: super( BaseTest, cls ).setUpClass( )
हैनेस

1
@ हँसियाँ कम से कम अजगर 3 में, केवल उपवर्गों में या उसके BaseTestमाध्यम से संदर्भित की जा सकती हैं , हालांकि स्पष्ट रूप से नहीं यदि आप निर्माणकर्ताओं को विरासत में देना चाहते थे । हो सकता है कि ऐसा "अनाम" विकल्प भी हो, जब आधार वर्ग को खुद को संदर्भित करने की आवश्यकता होती है (यह नहीं कि मुझे कोई विचार है जब एक वर्ग को संदर्भित करने की आवश्यकता होती है)। super(self.__class__, self)super()
स्टीन

28

मैथ्यू मार्शल का उत्तर बहुत अच्छा है, लेकिन इसके लिए आवश्यक है कि आप अपने प्रत्येक परीक्षा मामलों में दो वर्गों से भाग लें, जो त्रुटि-प्रवण है। इसके बजाय, मैं इसका उपयोग करता हूं (अजगर> = 2.7):

class BaseTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        if cls is BaseTest:
            raise unittest.SkipTest("Skip BaseTest tests, it's a base class")
        super(BaseTest, cls).setUpClass()

3
काफी अच्छा है। वहाँ एक तरीका है एक स्किप का उपयोग करने के लिए चारों ओर पाने के लिए है? मेरे लिए, स्केप्स अवांछनीय हैं और वर्तमान परीक्षण योजना (कोड या परीक्षण के साथ) में एक समस्या को इंगित करने के लिए उपयोग किया जाता है?
जैच यंग

@ZacharyYoung मुझे नहीं पता, शायद अन्य उत्तर मदद कर सकते हैं।
डेनिस गोलोमेज़ोव

@ZacharyYoung मैंने इस समस्या को ठीक करने का प्रयास किया है, मेरा उत्तर देखें।
सिमोनजैक

यह तुरंत स्पष्ट नहीं है कि दो वर्गों से विरासत में मिली त्रुटि के बारे में स्वाभाविक रूप से क्या त्रुटि है
jwg

@jw ने स्वीकृत उत्तर के लिए टिप्पणियां देखीं :) आपको अपने प्रत्येक परीक्षण वर्ग को दो आधार वर्गों से प्राप्त करने की आवश्यकता है; आपको उनमें से सही क्रम को संरक्षित करने की आवश्यकता है; क्या आपको एक और आधार परीक्षण वर्ग जोड़ना चाहिए, आपको उससे भी वारिस होना होगा। मिश्रणों के साथ कुछ भी गलत नहीं है, लेकिन इस मामले में उन्हें एक साधारण छोड़ दिया जा सकता है।
डेनिस गोलोमाज़ोव

7

आप क्या हासिल करने का प्रयास कर रहे हैं? यदि आपके पास सामान्य परीक्षण कोड (अभिकथन, टेम्प्लेट परीक्षण आदि) हैं, तो उन्हें उन विधियों में रखें, जिनके साथ उपसर्ग नहीं हैं, testइसलिए unittestउन्हें लोड नहीं किया जाएगा।

import unittest

class CommonTests(unittest.TestCase):
      def common_assertion(self, foo, bar, baz):
          # whatever common code
          self.assertEqual(foo(bar), baz)

class BaseTest(CommonTests):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(CommonTests):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)

class SubTest2(CommonTests):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

1
आपके सुझाव के तहत, उपवर्गों का परीक्षण करते समय सामान्य_सर्ट () अभी भी स्वचालित रूप से चलाया जाएगा?
स्टीवर्ट

@ स्टीवर्ट नहीं यह नहीं होगा। डिफ़ॉल्ट सेटिंग केवल "परीक्षण" से शुरू होने वाले तरीकों को चलाने के लिए है।
सीएस

6

मैथ्यू का जवाब वही है जिसका उपयोग मुझे तब से करना था जब मैं अभी भी 2.5 पर हूं। लेकिन 2.7 के रूप में आप किसी भी परीक्षण विधियों पर @ unittest.skip () डेकोरेटर का उपयोग कर सकते हैं जिसे आप छोड़ना चाहते हैं।

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

आधार प्रकार की जांच के लिए आपको अपने स्वयं के स्किपिंग डेकोरेटर को लागू करना होगा। पहले इस सुविधा का उपयोग नहीं किया गया था, लेकिन मेरे सिर के ऊपर से आप बेसटेस्ट का उपयोग स्किप करने के लिए मार्कर प्रकार के रूप में कर सकते हैं :

def skipBaseTest(obj):
    if type(obj) is BaseTest:
        return unittest.skip("BaseTest tests skipped")
    return lambda func: func

5

एक तरीका मैंने इसे हल करने के बारे में सोचा है वह परीक्षण विधियों को छिपाकर है यदि बेस क्लास का उपयोग किया जाता है। इस तरह परीक्षणों को छोड़ नहीं दिया जाता है, इसलिए कई परीक्षण रिपोर्टिंग टूल में पीले के बजाय परीक्षण के परिणाम हरे हो सकते हैं।

मिक्सिन विधि की तुलना में, पाइडर्म की तरह आइडेंट यह शिकायत नहीं करेगा कि यूनिट टेस्ट विधियां बेस क्लास से गायब हैं।

यदि एक आधार वर्ग इस वर्ग से विरासत में मिला है, तो इसे setUpClassऔर tearDownClassविधियों को ओवरराइड करना होगा ।

class BaseTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls._test_methods = []
        if cls is BaseTest:
            for name in dir(cls):
                if name.startswith('test') and callable(getattr(cls, name)):
                    cls._test_methods.append((name, getattr(cls, name)))
                    setattr(cls, name, lambda self: None)

    @classmethod
    def tearDownClass(cls):
        if cls is BaseTest:
            for name, method in cls._test_methods:
                setattr(cls, name, method)
            cls._test_methods = []

5

आप __test_ = Falseबेसटेस्ट क्लास में जोड़ सकते हैं , लेकिन यदि आप इसे जोड़ते हैं, तो अवगत रहें कि आपको __test__ = Trueपरीक्षण चलाने में सक्षम होने के लिए व्युत्पन्न कक्षाओं में जोड़ना होगा ।

import unittest

class BaseTest(unittest.TestCase):
    __test__ = False

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):
    __test__ = True

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):
    __test__ = True

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

4

एक अन्य विकल्प निष्पादित नहीं है

unittest.main()

इसके बजाय आप उपयोग कर सकते हैं

suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)

तो आप केवल कक्षा में परीक्षण निष्पादित करते हैं TestClass


यह सबसे कम हैकी समाधान है। unittest.main()डिफ़ॉल्ट सूट में एकत्रित किए गए को संशोधित करने के बजाय आप स्पष्ट सूट बनाते हैं और उसके परीक्षण चलाते हैं।
ज़गोडा

1

मैंने @Vladim P. ( https://stackoverflow.com/a/25695512/2451329 ) के बारे में समान बनाया है लेकिन थोड़ा संशोधित किया गया है:

import unittest2


from some_module import func1, func2


def make_base_class(func):

    class Base(unittest2.TestCase):

        def test_common1(self):
            print("in test_common1")
            self.assertTrue(func())

        def test_common2(self):
            print("in test_common1")
            self.assertFalse(func(42))

    return Base



class A(make_base_class(func1)):
    pass


class B(make_base_class(func2)):

    def test_func2_with_no_arg_return_bar(self):
        self.assertEqual("bar", func2())

और हम वहाँ जाते हैं।


1

पायथन 3.2 के रूप में, आप एक मॉड्यूल के लिए test_loader फ़ंक्शन को जोड़ सकते हैं ताकि यह पता लगाया जा सके कि परीक्षण खोज तंत्र द्वारा कौन से परीक्षण (यदि कोई हैं) पाए जाते हैं।

उदाहरण के लिए, निम्नलिखित केवल मूल पोस्टर SubTest1और SubTest2टेस्ट मामलों को लोड करेगा , अनदेखी Base:

def load_tests(loader, standard_tests, pattern):
    suite = TestSuite()
    suite.addTests([SubTest1, SubTest2])
    return suite

यह संभव है कि यह अधिक से अधिक पुनरावृत्त हो जाए standard_tests( TestSuiteपरीक्षण में डिफ़ॉल्ट लोडर पाया गया) और सभी Baseको suiteइसकी बजाय कॉपी करें , लेकिन नेस्टेड प्रकृति TestSuite.__iter__बनाता है कि यह बहुत अधिक जटिल है।


0

बस TestCommon विधि का नाम बदलकर कुछ और करें। यूनीटेस्ट (आमतौर पर) ऐसी किसी भी चीज़ को छोड़ देता है, जिसमें 'परीक्षण' नहीं होता है।

त्वरित और सरल

  import unittest

  class BaseTest(unittest.TestCase):

   def methodCommon(self):
       print 'Calling BaseTest:testCommon'
       value = 5
       self.assertEquals(value, 5)

  class SubTest1(BaseTest):

      def testSub1(self):
          print 'Calling SubTest1:testSub1'
          sub = 3
          self.assertEquals(sub, 3)


  class SubTest2(BaseTest):

      def testSub2(self):
          print 'Calling SubTest2:testSub2'
          sub = 4
          self.assertEquals(sub, 4)

  if __name__ == '__main__':
      unittest.main()`

2
यह सबटैस्ट में मेथोकॉमन टेस्ट नहीं चलाने का नतीजा होगा।
काली मिर्च लेबेक-जोबे

0

तो यह एक पुराने धागे की तरह है, लेकिन मैं आज इस समस्या में आया और इसके लिए अपनी खुद की हैक के बारे में सोचा। यह एक डेकोरेटर का उपयोग करता है जो बेस क्लास के माध्यम से स्वीकार किए जाने पर फ़ंक्शन के मूल्यों को बनाता है। सेटअप और सेटअपक्लास के बारे में चिंता करने की ज़रूरत नहीं है क्योंकि अगर बेसकेल्स का कोई परीक्षण नहीं है तो वे नहीं चलेंगे।

import types
import unittest


class FunctionValueOverride(object):
    def __init__(self, cls, default, override=None):
        self.cls = cls
        self.default = default
        self.override = override

    def __get__(self, obj, klass):
        if klass == self.cls:
            return self.override
        else:
            if obj:
                return types.MethodType(self.default, obj)
            else:
                return self.default


def fixture(cls):
    for t in vars(cls):
        if not callable(getattr(cls, t)) or t[:4] != "test":
            continue
        setattr(cls, t, FunctionValueOverride(cls, getattr(cls, t)))
    return cls


@fixture
class BaseTest(unittest.TestCase):
    def testCommon(self):
        print('Calling BaseTest:testCommon')
        value = 5
        self.assertEqual(value, 5)


class SubTest1(BaseTest):
    def testSub1(self):
        print('Calling SubTest1:testSub1')
        sub = 3
        self.assertEqual(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print('Calling SubTest2:testSub2')
        sub = 4
        self.assertEqual(sub, 4)

if __name__ == '__main__':
    unittest.main()

-2

बेसस्टेस्ट विधि नाम को सेटअप में बदलें:

class BaseTest(unittest.TestCase):
    def setUp(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)


class SubTest1(BaseTest):
    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):
    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

आउटपुट:

0.000 में रन 2 परीक्षण

कॉलिंग बेसटेस्ट: टेस्टकॉन कॉलिंग
सबटेस्ट 1: टेस्टसब 1 कॉलिंग
बेसटेस्ट: टेस्टकॉमन कॉलिंग
सबटेस्ट 2: टेस्टसब 2

से प्रलेखन :

TestCase.setUp ()
विधि जो कि परीक्षण निर्धारण तैयार करने के लिए कहा जाता है। परीक्षण विधि को कॉल करने से तुरंत पहले इसे कहा जाता है; इस पद्धति द्वारा उठाए गए किसी भी अपवाद को एक परीक्षण विफलता के बजाय एक त्रुटि माना जाएगा। डिफ़ॉल्ट कार्यान्वयन से कुछ भी नहीं होता है।


यह काम करेगा, क्या होगा अगर मेरे पास n टेस्टकॉमन है, तो क्या मुझे उन सभी को जगह देनी चाहिए setUp?
थियरी लैम

1
हां, आपको वह सभी कोड डालना चाहिए जो सेटअप के तहत वास्तविक परीक्षण मामला नहीं है।
ब्रायन आर। बॉंडी

लेकिन अगर एक उपवर्ग में एक से अधिक test...विधि setUpहो, तो ऐसी विधि के अनुसार, बार-बार निष्पादित की जाती है; तो यह वहाँ परीक्षण करने के लिए एक अच्छा विचार नहीं है!
एलेक्स मार्टेली

वास्तव में निश्चित नहीं है कि अधिक जटिल परिदृश्य में निष्पादित होने के समय ओपी क्या चाहता था।
ब्रायन आर। बॉन्डी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.