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

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

هناك مجموعتان من اختبارات الوحدات: اختبارات JavaScript واختبارات منشئ الكتل.

اختبارات JavaScript

تؤكد اختبارات JavaScript تشغيل دوال JavaScript الداخلية في نواة حظر. نستخدم Mocha لإجراء اختبارات الوحدات، وSinon لتقليل التبعيات، وChai للتأكد من صحة الرمز.

إجراء الاختبارات

في كلٍّ من العينات المجمّعة والكتلة، سيجري npm run test اختبارات الوحدات. في الغالب، سيؤدي ذلك أيضًا إلى إجراء اختبارات أخرى مثل تحويل المحتوى إلى كلام وتجميعه. يمكنك أيضًا فتح tests/mocha/index.html في متصفّح لإجراء جميع اختبارات Mocha بشكل تفاعلي.

اختبارات كتابة

نستخدم واجهة Mocha TDD لإجراء الاختبارات. يتم تنظيم الاختبارات في مجموعات، والتي يمكن أن تحتوي على أجنحة فرعية و/أو اختبارات إضافية. وبشكل عام، لكل مكوِّن من عناصر Blockly (مثل toolbox أو workspace) ملف اختبار خاص به يحتوي على مجموعة واحدة أو أكثر. يمكن أن تحتوي كل مجموعة على طريقة setup وteardown التي سيتم استدعاؤها قبل وبعد كل اختبار على التوالي في تلك المجموعة.

أدوات مساعدة الاختبار

لدينا عدد من الوظائف المساعدة الخاصة بتطبيق Blockly والتي قد تكون مفيدة عند كتابة الاختبارات. ويمكن العثور عليها في العيّنات الأساسية وفي النماذج المتوفرة فقط.

تشمل الوظائف المساعدة sharedTestSetup وsharedTestTeardown، وهما مطلوب من أجل طلب إجراء الاختبار قبل إجراء الاختبارات وبعدها (راجِع قسم "المتطلبات").

sharedTestSetup:
  • إعداد موقّتات sinon المزيفة (ستحتاج في بعض الاختبارات إلى استخدام this.clock.runAll).
  • StubsBlockly.Events.fire للتنشيط على الفور (قابل للتهيئة).
  • إعداد التنظيف التلقائي لأنواع الكتل المحدّدة على الرغم من defineBlocksWithJsonArray.
  • يوضح بعض السمات في سياق this التي من المفترض أن تكون متاحة للوصول إليها:
    • this.clock (ولكن يجب عدم استعادته، فسينتج عن ذلك مشاكل في sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (يُستخدم مع addMessageToCleanup وaddBlockTypeToCleanup) (ملاحظة: لن تحتاج إلى استخدام addBlockTypeToCleanup إذا تم تحديد الحظر باستخدام defineBlocksWithJsonArray)

تتضمن الدالة مَعلمة options اختيارية واحدة لضبط الإعدادات. وتُستخدَم في الوقت الحالي فقط لتحديد ما إذا كان يجب دفع Blockly.Events.fire لكي يتم تنشيطها فورًا (سيتم التخفّي تلقائيًا).

sharedTestTeardown:
  • يتم التخلص من مساحة العمل this.workspace (حسب المكان الذي تم تحديده فيها، راجع قسم "متطلبات الاختبار" لمزيد من المعلومات).
  • استعادة جميع التنويهات الموجزة.
  • تتيح لك هذه الميزة إزالة جميع أنواع المكعبات التي تمت إضافتها من خلال defineBlocksWithJsonArray وaddBlockTypeToCleanup.
  • يؤدي هذا الإجراء إلى إزالة جميع الرسائل التي تمت إضافتها حتى addMessageToCleanup.

متطلبات الاختبار

  • يجب أن يستدعي كل اختبار sharedTestSetup.call(this); باعتباره السطر الأول في إعداد المجموعة الخارجية وsharedTestTeardown.call(this); كسطر أخير في عملية تقسيم المجموعة الخارجية للملف.
  • إذا كنت بحاجة إلى مساحة عمل مع مجموعة أدوات عامة، يمكنك استخدام إحدى صناديق الأدوات المعدّة مسبقًا في الاختبار index.html. انظر أدناه للاطّلاع على مثال.
  • يجب التخلص من "this.workspace" بشكل صحيح. في معظم الاختبارات، عليك تحديد this.workspace في المجموعة الخارجية واستخدامه في جميع الاختبارات اللاحقة، ولكن في بعض الحالات قد تحدّده أو تعيد تعريفه في مجموعة داخلية (على سبيل المثال، يتطلّب أحد اختباراتك مساحة عمل بخيارات مختلفة عن الطريقة التي أعددتها في الأصل). يجب التخلص منها في نهاية الاختبار.
    • إذا حدّدت this.workspace في المجموعة الخارجية ولم تتم إعادة تعريفه مطلقًا، لن تحتاج إلى اتّخاذ أي إجراء آخر. وسيتم التخلص منها تلقائيًا بحلول "sharedTestTeardown".
    • إذا حدّدت this.workspace للمرة الأولى في مجموعة داخلية (على سبيل المثال، لم تحدِّده في المجموعة الخارجية)، عليك التخلص منه يدويًا من خلال طلب الرمز workspaceTeardown.call(this, this.workspace) عند إيقاف ذلك الجناح.
    • إذا عرّفت this.workpace في المجموعة الخارجية، ثم أعدت تعريفها في مجموعة اختبار داخلية، عليك أولاً طلب workspaceTeardown.call(this, this.workspace) قبل إعادة تعريفها لهدم مساحة العمل الأصلية المحددة في مجموعة المستوى الأعلى. عليك أيضًا التخلص من القيمة الجديدة يدويًا من خلال استدعاء workspaceTeardown.call(this, this.workspace) مرة أخرى ضمن عملية تقسيم هذه المجموعة الداخلية.

بنية الاختبار

تتبع اختبارات الوحدات بشكل عام بنية محددة، والتي يمكن تلخيصها بالترتيب والتنفيذ والتأكيد.

  1. الترتيب: عليك إعداد حالة العالم وأي شروط ضرورية للسلوك الذي يخضع للاختبار.
  2. التنفيذ: يمكنك استدعاء الرمز الذي يخضع للاختبار لتشغيل السلوك الذي يتم اختباره.
  3. تأكيد: عليك تقديم تأكيدات بشأن القيمة المعروضة أو التفاعلات مع العناصر الوهمية للتحقّق من الصحّة.

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

إليك مثال على ملف اختبار (تم تبسيطه من الشيء الحقيقي).

suite('Flyout', function() {
  setup(function() {
    sharedTestSetup.call(this);
    this.toolboxXml = document.getElementById('toolbox-simple');
    this.workspace = Blockly.inject('blocklyDiv',
        {
          toolbox: this.toolboxXml
        });
  });

  teardown(function() {
    sharedTestTeardown.call(this);
  });

  suite('simple flyout', function() {
    setup(function() {
      this.flyout = this.workspace.getFlyout();
    });
    test('y is always 0', function() {
      // Act and assert stages combined for simple test case
      chai.assert.equal(this.flyout.getY(), 0, 'y coordinate in vertical flyout is 0');
    });
    test('x is right of workspace if flyout at right', function() {
      // Arrange
      sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
        viewWidth: 100,
      });
      this.flyout.targetWorkspace.toolboxPosition = Blockly.TOOLBOX_AT_RIGHT;
      this.flyout.toolboxPosition_ = Blockly.TOOLBOX_AT_RIGHT;

      // Act
      var x = this.flyout.getX();

      // Assert
      chai.assert.equal(x, 100, 'x is right of workspace');
    });
  });
});

أشياء يجب ملاحظتها من هذا المثال:

  • ويمكن أن يحتوي الجناح على مجموعات أخرى تتضمّن طرق setup وteardown إضافية.
  • لكل جناح واختبار اسم وصفي.
  • تُستخدم تأكيدات شاي لتقديم تأكيدات حول التعليمة البرمجية.
    • يمكنك توفير وسيطة سلسلة اختيارية سيتم عرضها إذا فشل الاختبار. ويُسهِّل ذلك تصحيح أخطاء الاختبارات المعطّلة.
    • ترتيب المعلَمات هو chai.assert.equal(actualValue, expectedValue, optionalMessage). إذا بدّلت actual وexpected، لن تكون رسائل الخطأ منطقية.
  • يُستخدم Sinon لكبر الأساليب عندما لا تريد استدعاء الرمز الحقيقي. في هذا المثال، لا نريد استدعاء دالة المقاييس الحقيقية لأنها ليست ذات صلة بهذا الاختبار. نحن نهتم فقط بكيفية استخدام النتائج من خلال الطريقة قيد الاختبار. يستخدم Sinon الدالة getMetrics لعرض استجابة جاهزة يمكننا التحقق منها بسهولة في تأكيدات الاختبار.
  • يجب أن تحتوي طرق setup لكل مجموعة على إعداد عام فقط ينطبق على جميع الاختبارات. إذا كان اختبار سلوك معين يعتمد على حالة معينة، فيجب تحديد هذا الشرط بوضوح في الاختبار ذي الصلة.

اختبارات تصحيح الأخطاء

  • يمكنك فتح الاختبارات في متصفح واستخدام أدوات المطوّرين لتحديد نقاط التوقف والتحقّق ممّا إذا كانت اختباراتك تفشل بشكل غير متوقّع (أو تجتازها بشكلٍ غير متوقّع).
  • يجب ضبط .only() أو .skip() على اختبار أو مجموعة لتشغيل هذه المجموعة فقط من الاختبارات، أو يمكنك تخطّي الاختبار. مثال:

    suite.only('Workspace', function () {
      suite('updateToolbox', function () {
        test('test name', function () {
          // ...
        });
        test.skip('test I don’t care about', function () {
          // ...
        });
      });
    });
    

    تذكر إزالة هذه الأدوات قبل تنفيذ الكود الخاص بك.

حظر اختبارات المنشئ

لكل كتلة اختبارات وحدة خاصة بها. تتحقّق هذه الاختبارات من أنّ عمليات الحظر تنشئ رمزًا بدلاً من الوظائف على النحو المنشود.

  1. حمِّل tests/generators/index.html في Firefox أو Safari. تجدُر الإشارة إلى أنّ متصفّح Chrome وOpera لديهم قيود أمان تمنع تحميل الاختبارات من نظام "file://" المحلي (المشكلتان 41024 و47416).
  2. اختر الجزء المناسب من النظام لاختباره من القائمة المنسدلة، ثم انقر على "تحميل". من المفترض أن تظهر القوالب في مساحة العمل.
  3. انقر على "JavaScript".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في وحدة تحكّم JavaScript. إذا انتهى الإخراج بـ "OK"، فهذا يعني أن الاختبار قد تم بنجاح.
  4. انقر فوق "Python".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في مترجم Python. إذا انتهت النتيجة بـ "حسنًا"، يعني ذلك أنّ الاختبار قد اكتمل.
  5. انقر فوق "PHP".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في مترجم لغة PHP. إذا انتهت النتيجة بـ "حسنًا"، يعني ذلك أنّ الاختبار قد اكتمل.
  6. انقر فوق "Lua".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في مترجم Lua. إذا انتهت النتيجة بـ "حسنًا"، يعني ذلك أنّ الاختبار قد اكتمل.
  7. انقر فوق "Dart".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في مترجم Dart. إذا انتهت النتيجة بـ "حسنًا"، يعني ذلك أنّ الاختبار قد اكتمل.

تعديل اختبارات منشئ الكتل

  1. يجب تحميل "tests/generators/index.html" في متصفّح.
  2. اختر الجزء المناسب من النظام من القائمة المنسدلة، وانقر فوق "تحميل". من المفترض أن تظهر القوالب في مساحة العمل.
  3. أجرِ أي تغييرات أو إضافات على الوحدات.
  4. انقر فوق "XML".
  5. انسخ ملف XML الذي تم إنشاؤه في الملف المناسب في tests/generators/.