كيفية اختبار نفس التأكيد على كمية كبيرة من البيانات

StackOverflow https://stackoverflow.com/questions/3915232

سؤال

أنا أستخدم وحدة Python Unittest لإجراء عدد من الاختبارات ؛ ومع ذلك ، فهو متكرر للغاية.

لدي الكثير من البيانات التي أرغب في تشغيلها من خلال الاختبار نفسه مرارًا وتكرارًا ، والتحقق مما إذا كان صحيحًا. ومع ذلك ، لا بد لي من تحديد اختبار لكل واحد.

على سبيل المثال ، أريد أن أفعل شيئًا مشابهًا لهذا. أعلم أنه يمكنني القيام بذلك باستخدام مولد (وجدته في موضوع سابق هنا). ولكن هل هناك بدائل ، ربما حتى باستخدام وحدة اختبار مختلفة؟

أي اقتراح يمكن أن يكون عظيما.


import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            self.assertEqual(i, 33)
هل كانت مفيدة؟

المحلول 5

في منشور آخر تعثرت على مساحتهاختبارات الأنفإنه أكثر ملاءمة للاختبار الذي يحركه البيانات.


class Test_data():
    def testNumbers():
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            yield checkNumber, num

def checkNumber(num):
    assert num == 33

الكود أعلاه يفعل نفس الشيء بالضبط مثل مشاركتي الأولى. لا توجد حاجة إلى واردات ، فقط اكتب فئة بيثون.

تقوم بتنفيذ الاختبارات عن طريق الكتابة:

nosetests filename

نصائح أخرى

يمكن أن تبدو رمز عينة من الحلول المقترحة بواسطة Bill Gribble مثل هذا:

import unittest

class DataTestCase(unittest.TestCase):
    def __init__(self, number):
        unittest.TestCase.__init__(self, methodName='testOneNumber')
        self.number = number

    def testOneNumber(self):
        self.assertEqual(self.number, 33)

    def shortDescription(self):
        # We need to distinguish between instances of this test case.
        return 'DataTestCase for number %d' % self.number


def get_test_data_suite():
    numbers = [0,11,222,33,44,555,6,77,8,9999]
    return unittest.TestSuite([DataTestCase(n) for n in numbers])

if __name__ == '__main__':
    testRunner = unittest.TextTestRunner()
    testRunner.run(get_test_data_suite())

قد ترغب في التفكير في استخدام فئة unittest.testsuite ، والتي ستسمح لك ببناء مجموعة من مثيلات testcase بشكل ديناميكي والتي سيتم تشغيلها بشكل منفصل. يجب أن تحدد الفئة الفرعية unittest.testcase طريقة اختبار واحدة فقط ، مع قبول الفئة معلمة بناء تمر في القيمة للاختبار مقابل هذه الحالة بالذات.

ال ddt مكتبة تم تصميمه لحل ما تطلبه بالضبط unittest[*].

فمثلا:

import ddt
import unittest

@ddt.ddt
class EvalTests(unittest.TestCase):

    @ddt.data(
            ('1', 1),
            ('1 == 1',  True),
            ('1 == 2',  False),
            ('1 + 2',   4),  ## This will fail
    )
    def test_eval_expressions(self, case):
        expr, exp_value = case
        self.assertEqual(eval(expr), exp_value)

وعندما تقوم بتشغيله ، تحصل على 4 حالات اختبار بدلاً من واحدة فقط:

$ python -m unittest  -v  test_eval.py
test_eval_expressions_1___1___1_ (test_eval.EvalTests) ... ok
test_eval_expressions_2___1__1___True_ (test_eval.EvalTests) ... ok
test_eval_expressions_3___1__2___False_ (test_eval.EvalTests) ... ok
test_eval_expressions_4___1_2___4_ (test_eval.EvalTests) ... FAIL

======================================================================
FAIL: test_eval_expressions_4___1_2___4_ (test_eval.EvalTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python/lib/site-packages/ddt.py", line 129, in wrapper
    return func(self, *args, **kwargs)
  File "/Work/test_eval.py", line 15, in test_eval_expressions
    self.assertEqual(eval(expr), exp_value)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=1)

لاحظ أن الديمقراطي يحاول التوصل إلى أسماء ل TCS التي تم إنشاؤها.

تثبيته مع PIP:

pip install ddt

*] نفس الحل للثعبان pytest نطاق (pytest.mark.parametrize) تم دمجها في الأداة الأساسية ، وتستحق التحول إليها pytest فقط لهذه الميزة وحدها.

تكمن مشكلة تشغيل التأكيدات في حلقة في أنه إذا فشل أحد التأكيدات ، فأنت لا تعرف القيمة التي تسببت فيها (في مثالك ، ستفشل 0, ، لكنك لا تعرف ذلك حتى تصحح). من ناحية أخرى ، تكرار self.assertEqual(i, 33) هي فكرة أسوأ ، لأنها تقدم تكرار الكود.

ما أقوم به في اختباري هو إنشاء وظيفة داخلية بسيطة ، يتم تسميتها قريبًا داخل الاختبار والاتصال بها بحجج مختلفة. لذا فإن وظيفتك ستبدو هكذا:

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        def eq(i):
            self.assertEqual(i, 33)
        eq(0)
        eq(11)
        eq(222)
        eq(33)
        eq(44)
        eq(555)
        ... 

بهذه الطريقة ، عندما يفشل التأكيد 0, ، تراه على الفور على تتبع المكدس المطبوع بواسطة unittest وحدة.

اعتبارًا من Python 3.4 يمكنك استخدامه unittest.TestCase.subTest(msg=None, **params) مدير السياق (توثيق). سيتيح لك ذلك تحقيق ما تريده بإضافة بيان واحد فقط.

إليك مثالك المعدل للاستخدام subTest()

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0, 11, 222, 33, 44, 555, 6, 77, 8, 9999]
        for i in numbers:
            with self.subTest(i=i):  # added statement
                self.assertEqual(i, 33)

عرضية من هذه الجواب ، الذي لم ينجح لي تمامًا. حيث لا أتعامل مع كبير كميات من البيانات ، كنت بحاجة إلى إجراء نفس الاختبارات مع مدخلات مختلفة. الاختبارات التالية تستخدم create_a و create_b الطرق التي أريد تخصيصها.

الشرط هو إجراء كلا الاختبارين بنفس التخصيص.

class Tests(unittest.TestCase):

    def test_a_uses_b(self):
        a = create_a()
        b = create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = create_a()
        b = create_b()
        b.a = a
        self.assertIs(a.b, b)

إنشاء مثيل TestSuite و TestCase نفسي ، تجاوز محمل الاختبار ، أدى إلى خطأ لأنه توقع طريقة واحدة ، تسمى runTest.

وكانت النتيجة هذا:

class Tests(unittest.TestCase):

    def __init__(self, create_a, create_b):
        super().__init__()
        self.create_b = create_b
        self.create_a = create_a

    def test_a_uses_b(self):
        a = self.create_a()
        b = self.create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = self.create_a()
        b = self.create_b()
        b.a = a
        self.assertIs(a.b, b)


class TestPair1(Tests):
    def __init__(self):
        super().__init__(create_a1, create_b1)


class TestPair2(Tests):
    def __init__(self):
        super().__init__(create_a2, create_b2)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top