Advertisement
  1. Code
  2. Python

كتابة اختبارات الوحدة الفنية في Python

Scroll to top
Read Time: 20 min

() translation by (you can also view the original English article)

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

تشغيل المثال

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

1
class SelfDrivingCar(object):
2
3
    def __init__(self):
4
5
        self.speed = 0
6
7
        self.destination = None
8
9
        
10
11
    def _accelerate(self):
12
13
        self.speed += 1
14
15
        
16
17
    def _decelerate(self):
18
19
        if self.speed > 0:
20
21
            self.speed -= 1
22
23
                    
24
25
    def _advance_to_destination(self):
26
27
        distance = self._calculate_distance_to_object_in_front()
28
29
        if distance < 10:
30
31
            self.stop()
32
33
34
35
        elif distance < self.speed / 2:
36
37
            self._decelerate()
38
39
        elif self.speed < self._get_speed_limit():
40
41
            self._accelerate()
42
43
    
44
45
    def _has_arrived(self):
46
47
        pass
48
49
        
50
51
    def _calculate_distance_to_object_in_front(self):
52
53
        pass
54
55
        
56
57
    def _get_speed_limit(self):
58
59
        pass
60
61
        
62
63
    def stop(self):
64
65
        self.speed = 0
66
67
        
68
69
    def drive(self, destination):
70
71
        self.destination = destination
72
73
        while not self._has_arrived():            
74
75
            self._advance_to_destination()
76
77
78
        self.stop()
79
80
    def __init__(self):
81
82
        self.speed = 0
83
84
        self.destination = None
85
86
        
87
88
    def _accelerate(self):
89
90
        self.speed += 1
91
92
        
93
94
    def _decelerate(self):
95
96
        if self.speed > 0:
97
98
            self.speed -= 1
99
100
                    
101
102
    def _advance_to_destination(self):
103
104
        distance = self._calculate_distance_to_object_in_front()
105
106
        if distance < 10:
107
108
            self.stop()
109
110
111
112
        elif distance < self.speed / 2:
113
114
            self._decelerate()
115
116
        elif self.speed < self._get_speed_limit():
117
118
            self._accelerate()
119
120
    
121
122
    def _has_arrived(self):
123
124
        pass
125
126
        
127
128
    def _calculate_distance_to_object_in_front(self):
129
130
        pass
131
132
        
133
134
    def _get_speed_limit(self):
135
136
        pass
137
138
        
139
140
    def stop(self):
141
142
        self.speed = 0
143
144
        
145
146
    def drive(self, destination):
147
148
        self.destination = destination
149
150
        while not self._has_arrived():            
151
152
            self._advance_to_destination()
153
154
        self.stop()
155

هنا اختبار وحدة لأسلوب stop () لشحذ شهيتك. سوف ندخل التفاصيل لاحقاً.

1
from unittest import TestCase
2
3
4
5
class SelfDrivingCarTest(TestCase):
6
7
    def setUp(self):
8
9
        self.car = SelfDrivingCar()
10
11
        
12
13
    def test_stop(self):
14
15
        self.car.speed = 5
16
17
        self.car.stop()
18
19
        # Verify the speed is 0 after stopping

20
21
        self.assertEqual(0, self.car.speed)
22
23
        
24
25
        # Verify it is Ok to stop again if the car is already stopped

26
27
        self.car.stop()
28
29
        self.assertEqual(0, self.car.speed)

المبادئ التوجيهية لاختبار وحدة

ارتكاب

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

تكون منضبطة

يجب أن تكون منضبطة. أن تكون متسقة. تأكد من اجتياز الاختبارات دائماً. لا تدع الاختبارات تكون مكسورة لأنك "تعرف" التعليمة البرمجية هو موافق.

أتمتة

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

يتم تقسيم التعليمات البرمجية التي لم يتم اختبارها بتعريف

إذا لم يمكنك اختبار ذلك، لا يمكنك القول أنه يعمل. وهذا يعني أن عليك أن تنظر في ذلك كسر. إذا كانت التعليمات البرمجية الحرجة، لا نشر ذلك للإنتاج.

معلومات أساسية

ما هي وحدة؟

وحدة لغرض اختبار وحدة ملف/وحدة تحتوي على مجموعة من المهام ذات الصلة أو فئة. إذا كان لديك ملف مع فئات متعددة، يجب عليك كتابة اختبار وحدة لكل فئة.

TDD أو عدم TDD

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

والسبب أن أنا تصميم مع التعليمات البرمجية. اكتب التعليمات البرمجية، ننظر في الأمر وكتابتها، وننظر في الأمر مرة أخرى وإعادة كتابتها مرة أخرى بشكل سريع جداً. كتابة اختبارات أول تحد لي ويبطئ لي.

مرة واحدة وأنا فعلت مع التصميم الأولى، أنا اكتب الاختبارات على الفور، قبل الاندماج مع بقية النظام. وقال أن، أنها طريقة رائعة لتقديم نفسك لاختبارات الوحدة، ويضمن أنه سيكون كافة التعليمات البرمجية الخاصة بك للاختبارات.

الوحدة النمطية أونيتيست

الوحدة unittest أونيتيست يأتي مع مكتبة بيثون القياسية. يوفر فئة تسمى TestCase التي يمكنك اشتقاق الفئة الخاصة بك من. ثم يمكنك تجاوز أسلوب ()setUp أن تعد لاعبا اختبار قبل كل اختبار و/أو أسلوب فئة ()classSetUp لإعداد لاعبا اختبار لكافة الاختبارات (عدم إعادة تعيين بين الاختبارات الفردية). هي المقابلة هناك أساليب ()tearDown و ()classTearDown ويمكنك تجاوز كذلك.

وفيما يلي الأجزاء ذات الصلة من فئة SelfDrivingCarTest لدينا. استخدام الأسلوب ()setUp فقط. قم بإنشاء مثيل SelfDrivingCar طازجة، وتخزينها في self.car حيث أنها متاحة لكل اختبار.

1
from unittest import TestCase
2
3
4
5
class SelfDrivingCarTest(TestCase):
6
7
    def setUp(self):
8
9
        self.car = SelfDrivingCar()

والخطوة التالية كتابة أساليب الاختبار المحددة لاختبار التعليمات البرمجية تحت الاختبار-الدرجة SelfDrivingCar في هذه الحالة – القيام بما أنه من المفترض أن تفعل. الهيكل لأسلوب الاختبار معيار جميلة:

  • إعداد البيئة (اختياري).
  • إعداد النتيجة المتوقعة.
  • استدعاء التعليمات البرمجية تحت الاختبار.
  • ويؤكد أن النتيجة الفعلية تطابق النتيجة المتوقعة.

لاحظ أن النتيجة لا يجب أن يكون الإخراج من أسلوب. يمكن أن يكون تغيير دولة من فئة، وآثار الجانبية مثل إضافة صف جديد في قاعدة بيانات أو كتابة ملف أو إرسال رسالة بريد إلكتروني.

على سبيل المثال، لا يرجع الأسلوب ()stop من فئة SelfDrivingCar أي شيء، ولكن تتغير الحالة الداخلية بتعيين السرعة إلى 0. يتم استخدام الأسلوب ()assertEqual المقدمة من الفئة الأساسية في TestCase هنا للتحقق من أن تطلق على ()stop يعمل كما هو متوقع.

1
def test_stop(self):
2
3
        self.car.speed = 5
4
5
        self.car.stop()
6
7
        # Verify the speed is 0 after stopping

8
9
        self.assertEqual(0, self.car.speed)
10
11
        
12
13
        # Verify it is Ok to stop again if the car is already stopped

14
15
        self.car.stop()
16
17
        self.assertEqual(0, self.car.speed)

وهناك فعلا من اختبارين هنا. أول اختبار للتأكد من أنه إذا كانت سرعة السيارة 5 و ()stop يسمى، ثم يصبح السرعة 0. ثم، اختبار آخر التأكد من أي شيء يذهب على نحو خاطئ في حالة استدعاء ()stop مرة أخرى عند إيقاف السيارة الفعل.

في وقت لاحق، سوف أعرض عدة اختبارات أكثر للحصول على وظائف إضافية.

الوحدة النمطية دوكتيست

الوحدة النمطية دوكتيست مثيرة جداً للاهتمام. وهو يتيح لك استخدام نماذج التعليمات البرمجية التفاعلية في دوكسترينج الخاص بك وتحقق من النتائج، بما في ذلك الاستثناءات التي آثارها.

أنا لا استخدم أو يوصي دوكتيست لنظم على نطاق واسع. اختبار وحدة السليم يتطلب الكثير من العمل. اختبار التعليمات البرمجية عادة أكبر بكثير من التعليمات البرمجية تحت الاختبار. دوكسترينجس ليست فقط المتوسط الصحيح لكتابة الاختبارات الشاملة. فباردة، على الرغم. هنا ما يبدو وكأنه وظيفة factorial مع الاختبارات doc:

1
import math
2
3
4
5
def factorial(n):
6
7
    """Return the factorial of n, an exact integer >= 0.

8


9


10


11
    If the result is small enough to fit in an int, return an int.

12


13
    Else return a long.

14


15


16


17
    >>> [factorial(n) for n in range(6)]

18


19
    [1, 1, 2, 6, 24, 120]

20


21
    >>> [factorial(long(n)) for n in range(6)]

22


23
    [1, 1, 2, 6, 24, 120]

24


25
    >>> factorial(30)

26


27
    265252859812191058636308480000000L

28


29
    >>> factorial(30L)

30


31
    265252859812191058636308480000000L

32


33
    >>> factorial(-1)

34


35
    Traceback (most recent call last):

36


37
        ...

38


39
    ValueError: n must be >= 0

40


41


42


43
    Factorials of floats are OK, but the float must be an exact integer:

44


45
    >>> factorial(30.1)

46


47
    Traceback (most recent call last):

48


49
        ...

50


51
    ValueError: n must be exact integer

52


53
    >>> factorial(30.0)

54


55
    265252859812191058636308480000000L

56


57


58


59
    It must also not be ridiculously large:

60


61
    >>> factorial(1e100)

62


63
    Traceback (most recent call last):

64


65
        ...

66


67
    OverflowError: n too large

68


69
    """
70
71
    if not n >= 0:
72
73
        raise ValueError("n must be >= 0")
74
75
    if math.floor(n) != n:
76
77
        raise ValueError("n must be exact integer")
78
79
    if n+1 == n:  # catch a value like 1e300

80
81
        raise OverflowError("n too large")
82
83
    result = 1
84
85
    factor = 2
86
87
    while factor <= n:
88
89
        result *= factor
90
91
        factor += 1
92
93
    return result
94
95
96
97
98
99
if __name__ == "__main__":
100
101
    import doctest
102
103
    doctest.testmod()

كما ترون، أكبر بكثير من التعليمات البرمجية الدالة دوكسترينج. أنها لا تعزز قابلية القراءة.

تشغيل الاختبارات

كيلر وكتب لك اختبارات الوحدة الخاصة بك. لنظام كبيرة، سيكون لديك عشرات/مئات/آلاف من وحدات وفئات عبر ربما دلائل متعددة. كيف يمكنك تشغيل جميع هذه الاختبارات؟

الوحدة النمطية أونيتيست يوفر مرافق مختلفة لتجميع الاختبارات وتشغيلها برمجياً. تحقق من تحميل وتشغيل الاختبارات. ولكن أسهل طريقة اكتشاف الاختبار. تمت إضافة هذا الخيار فقط في 2.7 بيثون. ما قبل-2.7 يمكن استخدام nose لاكتشاف وتشغيل الاختبارات. وقد آنف بعض المزايا الأخرى مثل تشغيل اختبار وظائف دون الحاجة إلى إنشاء فئة لحالات الاختبار الخاصة بك. ولكن غرض هذه المقالة، دعونا العصا مع أونيتيست.

لاكتشاف وتشغيل الاختبارات الخاصة بك المستندة إلى أونيتيست، ببساطة اكتب في سطر الأوامر:

python -m unittest discover

سيتم مسح كافة الملفات والدلائل الفرعية أونيتيست وتشغيل أي الاختبارات التي يجدها وتقديم التقرير الجميل، فضلا عن وقت التشغيل. إذا كنت تريد أن ترى ما هي اختبارات تشغيله، يمكنك إضافة العلامة-v:

python -m unittest discover -v

هناك العديد من الإعلام التي تتحكم في هذه العملية:

1
python -m unittest -h
2
3
Usage: python -m unittest [options] [tests]
4
5
6
7
Options:
8
9
  -h, --help       Show this message
10
11
  -v, --verbose    Verbose output
12
13
  -q, --quiet      Minimal output
14
15
  -f, --failfast   Stop on first failure
16
17
  -c, --catch      Catch control-C and display results
18
19
  -b, --buffer     Buffer stdout and stderr during test runs
20
21
22
23
Examples:
24
25
  python -m unittest test_module               - run tests from test_module
26
27
  python -m unittest module.TestClass          - run tests from module.TestClass
28
29
  python -m unittest module.Class.test_method  - run specified test method
30
31
32
33
[tests] can be a list of any number of test modules, classes and test
34
35
methods.
36
37
38
39
Alternative Usage: python -m unittest discover [options]
40
41
42
43
Options:
44
45
  -v, --verbose    Verbose output
46
47
  -f, --failfast   Stop on first failure
48
49
  -c, --catch      Catch control-C and display results
50
51
  -b, --buffer     Buffer stdout and stderr during test runs
52
53
  -s directory     Directory to start discovery ('.' default)
54
55
  -p pattern       Pattern to match test files ('test*.py' default)
56
57
  -t directory     Top level directory of project (default to
58
59
                   start directory)
60
61
62
63
For test discovery all test modules must be importable from the top
64
65
level directory of the project.

اختبار التغطية

تغطية الاختبار حقل غالباً ما تهمل. التغطية يعني كم من التعليمات البرمجية الخاصة بك يتم فعلا اختبار من الاختبارات الخاصة بك. على سبيل المثال، إذا كان لديك دالة مع عبارة if-else ويمكنك اختبار فقط if كان فرع، ثم كنت لا تعرف ما إذا كان يعمل فرع else أم لا. في مثال التعليمة البرمجية التالي، يتحقق ()add الدالة نوع الوسيطات الخاصة بها. إذا كان كلا من الإعداد الصحيحة، فإنه يضيف فقط منهم.

إذا كان كلاهما السلاسل، فإنه يحاول تحويلها إلى إعداد صحيحة وتضيف إليها. إلا أنها تقوم بإصدار استثناء. الدالة ()test_add اختبار الدالة ()add مع الحجج التي إعداد صحيحة على حد سواء ومع الحجج التي يطفو والتحقق من السلوك الصحيح في كل حالة على حدة. ولكن تغطية الاختبار غير مكتملة. لم أكن اختبار حالة سلسلة الحجج. نتيجة لذلك ينجح الاختبار بنجاح، ولكن لم يكن اكتشف الخطأ المطبعي في الفرع حيث الحجج كلا سلاسل (انظر 'إينتج' هناك؟).

1
import unittest
2
3
4
5
def add(a, b):
6
7
    """This function adds two numbers a, b and returns their sum

8


9


10


11
    a and b may integers

12


13
    """
14
15
    if isinstance(a, int) and isinstance(b, int):
16
17
        return a + b
18
19
    elseif isinstance(a, str) and isinstance(b, str):
20
21
        return int(a) + intg(b)
22
23
    else:
24
25
        raise Exception('Invalid arguments')
26
27
28
29
class Test(unittest.TestCase):
30
31
    def test_add(self):
32
33
        self.assertEqual(5, add(2, 3))
34
35
        self.assertEqual(15, add(-6, 21))
36
37
        self.assertRaises(Exception, add, 4.0, 5.0)
38
39
40
41
unittest.main()       

هنا هو الإخراج:

1
----------------------------------------------------------------------
2
3
Ran 1 test in 0.000s
4
5
6
7
OK
8
9
10
11
Process finished with exit code 0

اختبارات وحدة التدريب العملي

كتابة اختبارات الوحدة القوة الصناعية ليست سهلة أو بسيطة. وهناك العديد من الأشياء للنظر والمقايضات بذل.

تصميم Testability

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

والخبر السار أن تصميم البرامج المناسبة ذات الأغراض العامة بالضبط ما تحتاجه ل testability. على وجه الخصوص، سوف تجعل التعليمات البرمجية وحدات يؤخذ جيدا، حيث أن كل مكون من مكونات مسؤولية واضحة ويتفاعل مع المكونات الأخرى عبر واجهات محددة تحديداً جيدا، كتابة اختبارات الوحدة جيدة من دواعي سروري.

على سبيل المثال، لدينا فئة SelfDrivingCar مسؤولة عن عملية رفيعة المستوى للسيارة: الذهاب، وقف، والتنقل. أنها طريقة ()calculate_distance_to_object_in_front التي لم تنفذ حتى الآن. ربما ينبغي أن تنفذها هذه الوظيفة نظام فرعي منفصل تماما. وقد تشمل قراءة البيانات من مختلف أجهزة الاستشعار، وتتفاعل مع غيرها من السيارات ذاتية القيادة، كدسة رؤية جهاز كله لتحليل الصور من الكاميرات متعددة.

دعونا نرى كيف يعمل هذا في الممارسة العملية. SelfDrivingCar سوف تقبل وسيطة تسمى object_detector التي تحتوي على أسلوب يسمى ()calculate_distance_to_object_in_front ، وأنه سيتم تفويض هذه الوظيفة لهذا الكائن. الآن، ليس هناك أي حاجة لوحدة اختبار هذا نظراً object_detector هو المسؤول (وينبغي اختبار) لأنه. كنت لا تزال تريد وحدة اختبار حقيقة أن كنت تستخدم في object_detector بشكل صحيح.

1
class SelfDrivingCar(object):
2
3
    def __init__(self, object_detector):
4
5
        self.object_detector
6
7
        self.speed = 0
8
9
        self.destination = None
10
11
                
12
13
    def _calculate_distance_to_object_in_front(self):
14
15
        return self.object_detector.calculate_distance_to_object_in_front()

التكاليف والفوائد

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

على سبيل المثال، لدينا فئة السيارات ذاتية القيادة أمر بالغ الأهمية الفائقة. إذا كان الأسلوب ()stop لا يعمل بشكل صحيح، لدينا سيارة ذاتية القيادة قد قتل الناس، وتدمير الممتلكات، وإفشال كل ذاتية القيادة سوق السيارات. إذا قمت بتطوير سيارة ذاتية قيادة، وأظن اختبارات الوحدة الخاصة بك للأسلوب ()stop سوف يكون قليلاً أكثر صرامة من الألغام.

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

اختبار عقلية

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

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

وظائف نقي

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

لماذا سهلة لاختبار؟ لأنه ليس هناك حاجة لتعيين بيئة خاصة لاختبار. يمكنك فقط تمرير الوسائط والنتيجة الاختبار. وتعلمون أيضا أن طالما لم يتم تغيير التعليمات البرمجية تحت الاختبار، اختبار الخاص بك لا يملك تغيير.

قارن ذلك إلى دالة تقوم بقراءة ملف تكوين XML. وسيكون الاختبار الخاص بك لإنشاء ملف XML وتمرير الملف به إلى التعليمات البرمجية تحت الاختبار. ليست صفقة كبيرة. ولكن لنفترض أن أحدهم قرر أن XML البغيضة، ويجب أن تكون كافة ملفات التكوين في JSON. أنها ممارسة أعمالهم وتحويل كافة ملفات التكوين إلى JSON. أنهم تشغيل كافة الاختبارات بما في ذلك الاختبارات الخاصة بك، وأنهم جميعا تمرير!

لماذا؟ لأنه لم يكن تغيير التعليمات البرمجية. أنها لا تزال تتوقع ملف تكوين XML، واختبار الخاص بك لا يزال يبني ملف XML لذلك. ولكن في الإنتاج، التعليمات البرمجية الخاصة بك سوف تحصل على ملف JSON، وأنها سوف تفشل في تحليل.

اختبار معالجة الخطأ

معالجة الخطأ هو آخر شيء ضروري لاختبار. كما أنها جزء من التصميم. من هو المسؤول عن صحة الإدخال؟ كل دالة والأسلوب ينبغي أن يكون واضحا حول هذا الموضوع. إذا كانت المسؤولية للدالة، فإنه يجب التحقق من مدخلاتها، ولكن إذا هو المسؤولية للمتصل ثم الدالة يمكن اذهبوا حول أعماله التجارية وتحمل الإدخال بشكل صحيح. سيتم ضمان صحة النظام العام قبل وبعد الاختبارات للطالب للتأكد من أن يمر فقط الإدخال الصحيح لوظيفة الخاص بك.

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

دعنا نفترض أن الوجهة من المفترض أن تكون مجموعة من خطوط الطول والعرض. هناك كل أنواع الاختبارات التي يمكن القيام به للتحقق من أنها صحيحة (مثل هو الوجهة في وسط البحر). لأغراضنا، دعونا فقط التأكد من أنها مجموعة عوامات في نطاق 0.0 إلى 90.0-180.0 إلى 180.0 لخط الطول والعرض.

هنا هو تحديث فئة SelfDrivingCar. أنا نفذت مسلي بعض الأساليب حبراً على ورق لأن استدعاء الأسلوب ()drive بعض هذه الطرق مباشرة أو غير مباشرة.

1
class SelfDrivingCar(object):
2
3
    def __init__(self, object_detector):
4
5
        self.object_detector = object_detector
6
7
        self.speed = 0
8
9
        self.destination = None
10
11
12
13
    def _accelerate(self):
14
15
        self.speed += 1
16
17
18
19
    def _decelerate(self):
20
21
        if self.speed > 0:
22
23
            self.speed -= 1
24
25
26
27
    def _advance_to_destination(self):
28
29
        distance = self._calculate_distance_to_object_in_front()
30
31
        if distance < 10:
32
33
            self.stop()
34
35
36
37
        elif distance < self.speed / 2:
38
39
            self._decelerate()
40
41
        elif self.speed < self._get_speed_limit():
42
43
            self._accelerate()
44
45
46
47
    def _has_arrived(self):
48
49
        return True
50
51
52
53
    def _calculate_distance_to_object_in_front(self):
54
55
        return self.object_detector.calculate_distance_to_object_in_front()
56
57
58
59
    def _get_speed_limit(self):
60
61
        return 65
62
63
64
65
    def stop(self):
66
67
        self.speed = 0
68
69
70
71
    def drive(self, destination):
72
73
        self.destination = destination
74
75
        while not self._has_arrived():
76
77
            self._advance_to_destination()
78
79
        self.stop()

لاختبار خطأ معالجة في الاختبار، وسوف تمرير وسيطة غير صالحة والتحقق من أنها مرفوضة بشكل صحيح. يمكنك القيام بذلك باستخدام في ()self.assertRaises أسلوب unittest.TestCase. ينجح هذا الأسلوب إذا كانت التعليمات البرمجية تحت الاختبار في الواقع تقوم بإصدار استثناء.

دعونا نرى ذلك في العمل. الأسلوب ()test_drive بتمرير خطوط الطول والعرض خارج النطاق الصالح وتتوقع الأسلوب ()drive لرفع استثناء.

1
from unittest import TestCase
2
3
from self_driving_car import SelfDrivingCar
4
5
6
7
8
9
class MockObjectDetector(object):
10
11
    def calculate_distance_to_object_in_front(self):
12
13
        return 20
14
15
16
17
18
19
class SelfDrivingCarTest(TestCase):
20
21
    def setUp(self):
22
23
        self.car = SelfDrivingCar(MockObjectDetector())
24
25
26
27
    def test_stop(self):
28
29
        self.car.speed = 5
30
31
        self.car.stop()
32
33
        # Verify the speed is 0 after stopping

34
35
        self.assertEqual(0, self.car.speed)
36
37
38
39
        # Verify it is Ok to stop again if the car is already stopped

40
41
        self.car.stop()
42
43
        self.assertEqual(0, self.car.speed)
44
45
46
47
    def test_drive(self):
48
49
        # Valid destination

50
51
        self.car.drive((55.0, 66.0))
52
53
54
55
        # Invalid destination wrong range

56
57
        self.assertRaises(Exception, self.car.drive, (-55.0, 200.0))

فشل الاختبار، لأن الأسلوب ()drive لا تحقق الوسيطات الخاصة بها لصحة ولا تقوم بإصدار استثناء. يمكنك الحصول على تقرير لطيفة مع معلومات كاملة حول ما فشلت، أين ولماذا.

1
python -m unittest discover -v
2
3
test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ... FAIL
4
5
test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok
6
7
8
9
======================================================================
10
11
FAIL: test_drive (untitled.test_self_driving_car.SelfDrivingCarTest)
12
13
----------------------------------------------------------------------
14
15
Traceback (most recent call last):
16
17
  File "/Users/gigi/PycharmProjects/untitled/test_self_driving_car.py", line 29, in test_drive
18
19
    self.assertRaises(Exception, self.car.drive, (-55.0, 200.0))
20
21
AssertionError: Exception not raised
22
23
24
25
----------------------------------------------------------------------
26
27
Ran 2 tests in 0.000s
28
29
30
31
FAILED (failures=1)

لإصلاح ذلك دعونا تحديث الأسلوب ()drive الذي تحقق فعلا من نطاق الوسيطات الخاصة بها:

1
def drive(self, destination):
2
3
        lat, lon = destination
4
5
        if not (0.0 <= lat <= 90.0):
6
7
            raise Exception('Latitude out of range')
8
9
        if not (-180.0 <= lon <= 180.0):
10
11
            raise Exception('Latitude out of range')
12
13
        
14
15
        self.destination = destination
16
17
        while not self._has_arrived():
18
19
            self._advance_to_destination()
20
21
        self.stop()

والآن، تمرير كافة الاختبارات.

1
python -m unittest discover -v
2
3
test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok
4
5
test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok
6
7
8
9
----------------------------------------------------------------------
10
11
Ran 2 tests in 0.000s
12
13
14
15
OK
16

اختبار الأساليب الخاصة

وينبغي اختبار كل دالة والأسلوب؟ على وجه الخصوص، ينبغي اختبار أساليب خاصة تسمى فقط بواسطة التعليمات البرمجية الخاصة بك؟ الإجابة عادة غير مرضية: "الأمر يتوقف".

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

كيفية تنظيم اختبارات الوحدة الخاصة بك

في نظام كبير، أنها ليست دائماً واضحة كيفية تنظيم الاختبارات الخاصة بك. يجب أن يكون لديك ملف واحد كبير مع كافة الاختبارات لمجموعة، أو ملف اختبار واحد لكل فئة؟ ينبغي أن تكون الاختبارات في نفس ملف التعليمات البرمجية تحت الاختبار، أو في نفس الدليل؟

هنا هو استخدام النظام. الاختبارات يجب أن تكون منفصلة تماما عن التعليمات البرمجية تحت الاختبار (ومن ثم لا تستخدم دوكتيست). ومن الناحية المثالية، ينبغي أن تكون التعليمات البرمجية الخاصة بك في مجموعة. وينبغي أن الاختبارات لكل حزمة في دليل مشابهة للحزمة الخاصة بك. في دليل الاختبارات، ينبغي أن يكون هناك ملف واحد لكل وحدة نمطية من الحزمة الخاصة بك اسمه <test_<module name

على سبيل المثال، إذا كان لديك ثلاث وحدات في الحزمة الخاصة بك: module_1.py و ، module_2.py و module_3.py، يجب عليك اختبار ثلاثة ملفات: test_module_1.py و test_module_2.py و test_module_3.py تحت الدليل الاختبارات.

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

الاستنتاج

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

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.