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

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

هناك مجموعتان من اختبارات الوحدات: اختبارات JavaScript واختبارات أداة إنشاء الكتل.

اختبارات JS

تؤكِّد اختبارات JavaScript تشغيل دوال JavaScript الداخلية في جوهرة Blockly. نستخدم Mocha لإجراء اختبارات الوحدات، وSinon لإزالة التبعيات، وChai للتأكيد على الرمز.

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

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

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

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

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

لدينا عدد من الدوالّ المساعِدة الخاصة بخدمة Blockly التي قد تكون مفيدة عند كتابة الاختبارات. يمكنك العثور على هذه المراجع في ملف برمجي في core وملف برمجي في blockly-samples.

تشمل الدوالّ المساعِدة sharedTestSetup وsharedTestTeardown اللتين يجب استدعاؤهما قبل الاختبارات وبعدها (راجِع القسم المتعلق بالمتطلبات).

sharedTestSetup:
  • لإعداد موقّتات وهمية في مكتبة sinon (في بعض الاختبارات، ستحتاج إلى استخدام this.clock.runAll).
  • مثيل Blockly.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. Assert: يجب تقديم تأكيدات بشأن القيمة المعروضة أو التفاعلات مع العناصر الوهمية للتحقق من الصحّة.

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

في ما يلي مثال على ملف اختبار (مبسّط عن الملف الفعلي).

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 لتقديم تأكيدات حول الرمز البرمجي.
    • يمكنك تقديم وسيطة سلسلة اختيارية سيتم عرضها في حال تعذّر اجتياز الاختبار. ويسهّل ذلك تصحيح أخطاء الاختبارات غير الصالحة.
    • ترتيب المَعلمات هو chai.assert.equal(actualValue, expectedValue, optionalMessage). في حال تبديل actual وexpected، لن تكون رسائل الخطأ منطقية.
  • يتم استخدام 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".
    انسخ الرمز الذي تم إنشاؤه وشغِّله في مترجم سهم. إذا انتهت النتيجة بكلمة "حسنًا"، يعني ذلك أنّك اجتزت الاختبار.

تعديل اختبارات أداة إنشاء الوحدات

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