Klara هي أدوات تحليل ثابتة لإنشاء حالة اختبار تلقائية ، استنادًا إلى SMT (Z3) Solver ، مع نظام استنتاج قوي على مستوى AST. ستأخذ Klara ملف Python كمدخل وإنشاء ملف اختبار مقابل بتنسيق Pytest ، والذي يحاول تغطية جميع قيم الإرجاع. على سبيل المثال ، الوظيفة التالية في ملف test.py
def triangle ( x : int , y : int , z : int ) -> str :
if x == y == z :
return "Equilateral triangle"
elif x == y or y == z or x == z :
return "Isosceles triangle"
else :
return "Scalene triangle"سوف تولد
import test
def test_triangle_0 ():
assert test . triangle ( 0 , 0 , 0 ) == 'Equilateral triangle'
assert test . triangle ( 0 , 0 , 1 ) == 'Isosceles triangle'
assert test . triangle ( 2 , 0 , 1 ) == 'Scalene triangle'انظر وثائق Klara في https://klara-py.readthedocs.io
ملاحظة : لا تزال Klara في المرحلة التجريبية المبكرة ، والميزات المفقودة الملحوظة هي الحلقة والفهم واستيراد الوحدة النمطية والاستثناءات وغيرها الكثير. انظر القيود على القائمة الكاملة. من المحتمل ألا يتم تشغيله في مشاريع العالم الحقيقي ، لذلك من الأفضل اختيار بعض الوظائف المثيرة للاهتمام لإنشاء حالة الاختبار المقابلة.
يمكن تثبيت Klara عبر أداة pip باستخدام:
pip install klara
يمكننا استدعاء klara على أي ملف مصدر Python ، وسيقوم بإنشاء ملف اختبار Pytest المقابل.
$ cat source.py
def foo(x: int, y: int, z: str):
if x + y > 2:
return x + y + 12
elif x < y:
return x + y
elif (z + " me " ) == " some " :
return z + " thing "
else:
return x - y
$ klara source.py
$ cat test_source.py
import contract_test
def test_foo_0 ():
assert contract_test.foo(0, 3, '' ) == 15
assert contract_test.foo(0, 1, '' ) == 1
assert contract_test.foo(0, 0, ' so ' ) == ' sothing '
assert contract_test.foo(0, 0, '' ) == 0استشر دليل البدء السريع لمزيد من الأمثلة والتوجيه. لاستخدامها كمكتبة تحليل ثابت ، انتقل إلى الاستدلال.
يعمل Klara على مستوى AST ولا يقوم بتنفيذ رمز المستخدم بأي شكل من الأشكال ، وهو فرق مهم للغاية مقارنة بأداة مماثلة مثل Crosshair و Pynguin التي تستخدم التنفيذ الرمزي الوسيط الذي يتطلب تنفيذ رمز المستخدم الذي قد يسبب آثارًا جانبية غير مرغوب فيها. تعمل Klara على مستوى AST ، وتجمع مع تحليل تدفق البيانات الذي يستخدم رسم تخفيض للتحكم (CFG) ، والتعيين الفردي الثابت (SSA) ، وسلسلة الاستخدام-DEF ، وما إلى ذلك ... لإنشاء نظام استنتاج قوي للثعبان يستفيد من وحدة الحل Z3 لحل القيود والتحقق من جدوى المسار. ولهذا السبب ، فإن Klara قادر على العمل على كود المصدر Python2/3 بمساعدة typed_ast. لتحديد رمز المصدر في Python 2 ، تمرير في الوسيطة -py 2 . إنه بيثون 3 افتراضيًا.
يمكن أيضًا استخدام Klara كأداة تحليل ثابت ، اسمح للمستخدم بتحديد القاعدة المخصصة لتحديد أخطاء البرمجة أو الخطأ أو فرض معيار الترميز. مع دعم SMT Solver ، سيكون التحليل أكثر دقة ويقلل بشكل كبير من الحالة الإيجابية الخاطئة. على سبيل المثال
import klara
tree = klara . parse ( """
def foo(v1: int):
if v1 > 4:
if v1 < 3:
z = 1
else:
z = 2
else:
z = 3
s = z
""" )
with klara . MANAGER . initialize_z3_var_from_func ( tree . body [ 0 ]):
print ( list ( tree . body [ 0 ]. body [ - 1 ]. value . infer ()))سوف يطبع:
[2, 3]
لأن z = 1 غير ممكن بسبب v1 > 4 و v1 < 3 غير مرض
إن بنية نظام الاستدلال وواجهة برمجة التطبيقات مستوحاة إلى حد كبير من Astroid ، وهي مكتبة استنتاج ثابت يستخدمها Pylint.
تستخدم Klara نظام الاستدلال لإنشاء حالة اختبار ، وبعبارة أخرى ، فإنه يولد حالة اختبار لجميع قيم الإرجاع الممكنة للوظيفة ، بدلاً من إنشاء حالة اختبار لجميع مسار التحكم في الوظيفة.
لتوضيح هذه النقطة ، فكر في الوظيفة أدناه ، مع divide by zero في السطر 3
def foo ( v1 : int , v2 : float ):
if v1 > 10000 :
s = v1 / 0 # unused statement
if v1 > v2 :
s = v1
else :
s = v2
return sستقوم Klara بإنشاء مدخلات اختبار أدناه
import contract_test
def test_foo_0 ():
assert contract_test . foo ( 0 , - 1.0 ) == 0
assert contract_test . foo ( 0 , 0.0 ) == 0.0 لا يولد إدخال v1 > 10000 ، وبالتالي فإن حالة الاختبار لن تتمكن من معرفة الاستثناءات. هذا لأن s في السطر 3 غير مستخدم في قيمة الإرجاع.
إذا قمنا بتعديل البيان If IF إلى elif ، والتي سنكون قادرين على إعادة [s] {. title-ref} في السطر 3 ، ستقوم Klara بإنشاء مدخلات اختبار تغطي v1 > 10000 .
هذا تمييز مهم مع توليد حالة الاختبار التلقائي الأخرى المتاحة الآن ، لأنه من خلال إنشاء حالة اختبار فقط لقيم الإرجاع ، يمكننا إنشاء حالة اختبار الحد الأدنى ، ومن الأسهل تخصيص كيفية تغطي Klara الوظيفة.
على سبيل المثال ، قل أننا نؤلف نظامًا معقدًا
def main ( number : int , cm : int , dc : int , wn : int ):
mc = 0
if wn > 2 :
if number > 2 and number > 2 or number > 2 :
if number > 0 :
if wn > 2 or wn > 2 :
mc = 2
else :
mc = 5
else :
mc = 100
else :
mc = 1
nnn = number * cm
if cm <= 4 :
num_incr = 4
else :
num_incr = cm
n_num_incr = nnn / num_incr
nnn_left = dc * num_incr * ( n_num_incr / 2 + n_num_incr % 2 )
nnn_right = nnn - nnn_left
is_flag = nnn_right
if is_flag :
cell = Component ( nnn_right , options = [ mc ])
else :
cell = Component ( nnn_right )
return cellليس من الواضح لنا على الفور عدد قيم الإرجاع الممكنة الموجودة. ولكن يمكننا استخدام Klara لإنشاء مدخلات على الفور ، أدناه هو الاختبار الذي تم إنشاؤه
import contract_test
def test_main_0 ():
assert contract_test . main ( 2 , 4 , 1 , 3 ) is not None
assert contract_test . main ( 2 , 4 , - 1 , 6 ) is not None
assert contract_test . main ( 2 , 4 , 1 , 4 ) is not None
assert contract_test . main ( - 2 , 4 , 3 , 4 ) is not None
assert contract_test . main ( - 1 , - 1 , - 1 , 2 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 3 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 6 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 4 ) is not None
assert contract_test . main ( - 2 , 0 , 0 , 4 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 0 ) is not None تم إنشاء 10 نتائج إجمالية أعلاه ، وهي نتاج nnn_right والتي لها إمكانيات و mc التي لديها 5 إمكانيات.
لنفترض أن إدخال 10 اختبارات أكثر من اللازم ، وقد حددنا أن وسيطة options Component زائدة عن الحاجة للاختبار ، يمكننا استخدام المكون الإضافي المخصص لـ Klara لتحديد الجزء الذي يجب تجاهله بشكل انتقائي في توليد الاختبار. انتقل إلى تخصيص استراتيجية التغطية لمزيد من المعلومات.
بعد أن نقوم بإعداد المكون الإضافي ، ستنشئ Klara الاختبار التالي
import contract_test
def test_main_0 ():
assert contract_test . main ( 1 , 3 , 0 , 0 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 0 ) is not None ما هو فقط مجموعتين من nnn_right
نظرًا لأن Klara لا يمكنه تنفيذ الرمز بشكل ديناميكي ، فسيوفر امتدادًا لتحديد كيفية استنتاج عقدة AST المحددة أو النوع المحدد للمستخدم لجعل Klara "أكثر ذكاءً". تم وصفه في تمديد وتوسيع نوع المستخدم وتخصيص استراتيجية التغطية.
نستخدم الشعر لإدارة التبعيات. بعد تثبيت الشعر ، قم بتشغيل:
$ poetry shell
$ poetry install
لتشغيل حالة الاختبار ، افعل:
$ poetry run pytest test
تم ترخيص هذا المشروع بموجب شروط رخصة GNU Lesser العامة العامة.