كيفية التوقف عن القلق والبدء في كتابة الاختبارات القائمة على الممتلكات

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

اختبار تخزين القيمة الرئيسية في ثلاثة اختبارات قصيرة


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

  • اكتب القيمة بالمفتاح
  • تحقق مما إذا كان هناك إدخال بالمفتاح المرغوب
  • قراءة القيمة حسب المفتاح
  • الحصول على قائمة العناصر المسجلة
  • الحصول على نسخة من مستودع

في الطريقة الكلاسيكية القائمة على المثال ، سيبدو الاختبار النموذجي مثل هذا:

storage = Storage() storage['a'] = 42 assert len(storage) == 1 assert 'a' in storage assert storage['a'] == 42 

أو هكذا:

 storage = Storage() storage['a'] = 42 storage['b'] = 73 assert len(storage) == 2 assert 'a' in storage assert 'b' in storage assert storage['a'] == 42 assert storage['b'] == 73 

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

 storage = Storage() key = arbitrary_key() value = arbitrary_value() storage[key] = value assert len(storage) == 1 assert key in storage assert storage[key] == value 

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

 storage = arbitrary_storage() storage_copy = storage.copy() assert len(storage) == len(storage_copy) assert all(storage_copy[key] == storage[key] for key in storage) assert all(storage[key] == storage_copy[key] for key in storage_copy) 

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

 storage = arbitrary_storage() backup = storage.copy() key = arbitrary_key() value = arbitrary_value() if key in storage: return storage[key] = value assert len(storage) == len(backup) + 1 assert key in storage assert storage[key] == value assert all(storage[key] == backup[key] for key in backup) 

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

  • نجد الخصائص
  • التحقق من الخصائص على كومة من البيانات المختلفة
  • ربح!

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

كل هذا جيد وجيد ، ولكن ...


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

 input = arbitrary_list() output = sort(input) assert all(a <= b for a, b in zip(output, output[1:])) 

ومثل هذا التنفيذ من ذلك سوف تمر تماما

 def sort(input): return [1, 2, 3] 

آمل أن يكون المعنوي هنا واضحًا.

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

الآن فيما يتعلق بمسألة الأطر ، ما الذي يمكن توقعه منها والسبب في الحاجة إليها على الإطلاق - بعد كل شيء ، لا يحظر أحد يديك لإجراء اختبار في دورة ما ، مما يؤدي إلى العشوائية في الداخل والاستمتاع بالحياة. في الحقيقة ، سيكون الفرح حتى سقوط الاختبار الأول ، وهو جيد إذا كان محليًا ، وليس في بعض CI. أولاً ، نظرًا لأن الاختبارات القائمة على الممتلكات عشوائية ، فأنت تحتاج بالتأكيد إلى طريقة لإعادة إنتاج الحالة التي تم إسقاطها بشكل موثوق ، وأي إطار عمل يحترم نفسه يسمح لك بالقيام بذلك. تتمثل الطرق الأكثر شيوعًا في إخراج نواة معينة إلى وحدة التحكم ، والتي يمكنك إزالتها يدويًا في عداء الاختبار وتشغيل العبوة التي تم إسقاطها (بشكل مناسب للتصحيح) ، أو إنشاء ذاكرة تخزين مؤقت على القرص باستخدام sids "تالف" ، والتي سيتم فحصها تلقائيًا عند بدء الاختبار ( يساعد في التكرار في CI). جانب مهم آخر هو تصغير البيانات (تقلص المصادر الأجنبية). نظرًا لأن البيانات يتم إنشاؤها بشكل عشوائي ، فهذه فرصة غير مزيفة تمامًا للوقوف على حالة اختبار تساقط مع حاوية مكونة من 1000 عنصر ، والتي لا تزال "من دواعي سرورها" تصحيح الأخطاء. لذلك ، فإن الأطر الجيدة بعد العثور على حالة feylyaschy تطبق عددًا من الأساليب البحثية في محاولة للعثور على مجموعة مدمجة أكثر من بيانات الإدخال ، والتي ستستمر في تعطل الاختبار. وأخيرًا - غالبًا ما يكون نصف وظيفة الاختبار عبارة عن مولد بيانات إدخال ، لذا فإن وجود المولدات المدمجة والأدوات الأولية التي تسمح لك ببناء مولدات أكثر تعقيدًا من مولدات بسيطة يساعد كثيرًا أيضًا.

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

 data = totally_arbitrary_data() perform_actions(sut, data) if is_category_a(data): assert property_a_holds(sut) else if is is_category_b(data): assert property_b_holds(sut) 

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

 data = totally_arbitrary_data() assume(is_category_a(data)) perform_actions(sut, data) assert property_a_holds(sut) 

و

 data = data_from_category_b() perform_actions(sut, data) assert property_b_holds(sut) 

خصائص مفيدة ، وموائلها


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

على الأقل لا تسقط


الخيار الأسهل هو نقل البيانات التعسفية إلى النظام قيد الاختبار والتحقق من عدم تعطلها. في الواقع ، هذا اتجاه منفصل تمامًا مع دمج الاسم المألوف ، حيث توجد أدوات متخصصة (على سبيل المثال AFL aka American Fuzzy Lop) ، ولكن مع بعض الامتدادات ، يمكن اعتباره حالة خاصة للاختبار بناءً على الخصائص ، وإذا لم تكن هناك أفكار على الإطلاق إذا لم يكن التسلق ، فيمكنك البدء به. ومع ذلك ، كقاعدة عامة ، نادراً ما تكون مثل هذه الاختبارات صائبة منطقية ، نظرًا لأن الانخفاضات المحتملة عادة ما تكون جيدة جدًا عند التحقق من الخصائص الأخرى. الأسباب الرئيسية وراء ذكر هذه "الخاصية" هي توجيه القارئ إلى fuzzers وخاصة AFL (هناك الكثير من المقالات باللغة الإنجليزية حول هذا الموضوع) ، حسناً ، لاستكمال الصورة.

اختبار أوراكل


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

 input = arbitrary_list() assert quick_sort(input) == bubble_sort(input) 

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

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

المتطلبات والثوابت


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

قائمة صغيرة من الأمثلة من مجموعة متنوعة من المناطق المناسبة لفحص الخصائص:

  • يجب أن يحتوي حقل الفصل على قيمة تم تعيينها مسبقًا (getter-setters)
  • يجب أن يكون المستودع قادراً على قراءة عنصر مسجل مسبقًا
  • لا تؤثر إضافة عنصر غير موجود مسبقًا إلى المستودع على العناصر المضافة مسبقًا
  • في العديد من القواميس ، لا يمكن تخزين عدة عناصر مختلفة لها نفس المفتاح
  • يجب ألا يكون ارتفاع الشجرة المتوازن أكثر سجل $ K \ cdot (N) $ اين N - عدد العناصر المسجلة
  • نتيجة الفرز هي قائمة العناصر المطلوبة
  • يجب أن تحتوي نتائج تشفير base64 على أحرف base64 فقط
  • يجب أن تُرجع خوارزمية إنشاء المسار سلسلة من الحركات المسموح بها والتي ستؤدي من النقطة A إلى النقطة B
  • لجميع النقاط من العزلات التي شيدت ينبغي أن يكون راضيا f(x،y)=const،
  • يجب أن ترجع خوارزمية التحقق من التوقيع الإلكتروني إلى " صحيح" إذا كان التوقيع حقيقيًا وخطأ
  • نتيجة لتقويم العظام ، يجب أن يكون لكل المتجهات في الأساس طول وحدة وعدد صفر من منتجات العدد المتبادل
  • يجب أن لا يغير طول ناقل الحركة وعمليات الدوران

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

الحث واختبار الدولة


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

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

تشبه إلى حد كبير الحث الرياضي:

  • يثبت البيان 1
  • إثبات العبارة N + 1 ، على افتراض أن العبارة N صحيحة

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

ذهابا وإيابا


إذا كانت هناك حاجة فجأة لاختبار بضع وظائف للتحويل المباشر والعكس لبعض البيانات ، ففكر في أنك محظوظ جدًا:

 input = arbitrary_data() assert decode(encode(input)) == input 

عظيم للاختبار:

  • التسلسل إلغاء التسلسل
  • تشفير التشفير
  • ترميز فك التشفير
  • تحويل المصفوفة الأساسية إلى رباعي والعكس
  • تنسيق إحداثي مباشر وعكسي
  • تحويل فورييه المباشر والعكسي

حالة خاصة ، ولكن مثيرة للاهتمام هو انعكاس:

 input = arbitrary_data() assert invert(invert(input)) == input 

مثال صارخ هو عكس أو تبديل المصفوفة.

العاطفة


بعض العمليات لا تغير نتيجة الاستخدام المتكرر. أمثلة نموذجية:

  • الفرز
  • أي تطبيع للمتجهات والقواعد
  • إعادة إضافة عنصر موجود إلى مجموعة أو قاموس
  • إعادة تسجيل نفس البيانات في بعض خصائص الكائن
  • إرسال البيانات إلى نموذج أساسي (المساحات في JSON تؤدي إلى نمط موحد على سبيل المثال)

يمكن أيضًا استخدام Idempotency لاختبار تسلسل إلغاء التسلسل إذا كان فك الترميز المعتاد (encode (input)) == طريقة الإدخال غير مناسبة بسبب تمثيلات ممكنة مختلفة لبيانات الإدخال المكافئة (مرة أخرى ، مسافات إضافية في بعض JSON):

 def normalize(input): return decode(encode(input)) input = arbitrary_data() assert normalize(normalize(input)) == normalize(input) 

طرق مختلفة ، نتيجة واحدة


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

 a = arbitrary_value() b = arbitrary_value() assert a + b == b + a 

قد يبدو الأمر بسيطًا ، ولكن هذه طريقة رائعة لاختبار:

  • إضافة وضرب الأرقام في تمثيل غير قياسي (bigint ، عقلاني ، هذا كل شيء)
  • "إضافة" النقاط على المنحنيات الإهليلجية في الحقول المحدودة (مرحبا ، تشفير!)
  • اتحاد مجموعات (والتي يمكن أن تحتوي على هياكل بيانات غير تافهة بالكامل)

بالإضافة إلى ذلك ، فإن إضافة عناصر إلى القاموس له نفس الخاصية:

 A = dict() A[key_a] = value_a A[key_b] = value_b B = dict() B[key_b] = value_b B[key_a] = value_a assert A == B 

الخيار أكثر تعقيدًا - لقد فكرت لفترة طويلة في وصفه بالكلمات ، ولكن لم يتبق سوى رمز رياضي. بشكل عام ، مثل هذه التحولات شائعة f(x) التي تحمل الممتلكات f(x+y)=f(x) cdotf(y) وليست كل من الوسيطة ونتائج الوظيفة بالضرورة مجرد رقم ، بل عمليات + و  cdot - فقط بعض العمليات الثنائية على هذه الأشياء. ما يمكنك اختبار مع هذا:

  • الجمع والضرب لجميع أنواع الأعداد الغريبة والمتجهات والمصفوفات والرباعيات ( a cdot(x+y)=a cdotx+a cdoty )
  • العوامل الخطية ، وخاصة جميع أنواع التكاملات ، والفوارق ، والتلافيق ، والمرشحات الرقمية ، وتحويلات فورييه ، وما إلى ذلك ( F[x+y]=F[x]+F[y] )
  • عمليات على كائنات مماثلة في تمثيلات مختلفة ، على سبيل المثال

    • M(qa cdotqb)=M(qa) cdotM(qb) اين qa و qb هي رباعيات واحدة ، و م(ف) - عملية تحويل رباعي إلى مصفوفة قاعدة مكافئة
    • F[a circb]=F[a] cdotF[b] اين دولا و ب هي إشارات  circ - الإلتواء  cdot - الضرب ، و F - تحويل فورييه


مثال على مهمة "عادية" أكثر قليلاً - لاختبار بعض خوارزمية دمج القاموس صعبة ، يمكنك القيام بشيء من هذا القبيل:

 a = arbitrary_list_of_kv_pairs() b = arbitrary_list_of_kv_pairs() result = as_dict(a) result.merge(as_dict(b)) assert result == as_dict(a + b) 

بدلا من الاستنتاج


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


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

Source: https://habr.com/ru/post/ar434008/


All Articles