בדיקות יחידה

אחרי שמוסיפים קוד או משנים אותו, כדאי להריץ את בדיקות היחידות הקיימות ולשקול לכתוב בדיקות נוספות. כל הבדיקות מתבצעות בגרסאות הלא דחוסות של הקוד.

יש שתי קבוצות של בדיקות יחידה: בדיקות JS ובדיקות של הכלי ליצירת בלוקים.

בדיקות JS

בדיקות ה-JS מאמתות את הפעולה של פונקציות JavaScript פנימיות בליבה של Blockly. אנחנו משתמשים ב-Mocha כדי להריץ בדיקות יחידה, ב-Sinon כדי לצמצם יחסי תלות, וב-Chai בטענות נכונות (assertions) לגבי הקוד.

הרצת בדיקות

גם בדגימות בלוקים וגם בדגימות בלוקים, npm run test יריץ את בדיקות היחידה. ב-Blockly, הפעולה הזו תפעיל גם בדיקות אחרות, כמו בדיקת שגיאות בקוד (linting) וקמפלקציה. אפשר גם לפתוח את tests/mocha/index.html בדפדפן כדי להריץ באופן אינטראקטיבי את כל הבדיקות של Mocha.

מבחנים בכתיבה

אנחנו משתמשים בממשק Mocha TDD כדי להריץ בדיקות. הבדיקות מסודרות בסוויטות, שיכולות להכיל יחידות משנה נוספות או בדיקות. באופן כללי, לכל רכיב של Blockly (כמו toolbox או workspace) יש קובץ בדיקה משלו שמכיל חבילה אחת או יותר. לכל חבילת בדיקות יכולות להיות שיטות setup ו-teardown שיופעלו לפני ואחרי כל בדיקה בחבילה, בהתאמה.

כלי עזר לבדיקות

יש לנו כמה פונקציות עזר ספציפיות ל-Blockly שיכולות להיות שימושיות כשכותבים בדיקות. תוכלו למצוא אותם בליבה וב-blockly-Sample.

פונקציות העזרה כוללות את sharedTestSetup ו-sharedTestTeardown, שחובה להפעיל לפני ואחרי הבדיקות (ראו הקטע 'דרישות').

sharedTestSetup:
  • הגדרת טיימרים מזויפים של Sinon (בחלק מהבדיקות תצטרכו להשתמש ב-this.clock.runAll).
  • Stubs Blockly.Events.fire להפעלה מיידית (ניתן להגדרה).
  • מגדיר ניקוי אוטומטי של בלוקים שהוגדרו באמצעות defineBlocksWithJsonArray.
  • מצהירה על מספר מאפיינים בהקשר של this שאמורים להיות נגישים:
    • this.clock (אבל לא צריך לשחזר אותו, אחרת הוא יגרום לבעיות ב-sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (לשימוש עם addMessageToCleanup ו-addBlockTypeToCleanup) (הערה: אין צורך להשתמש ב-addBlockTypeToCleanup אם הגדרתם את הבלוק באמצעות defineBlocksWithJsonArray)

לפונקציה יש פרמטר options אופציונלי אחד להגדרת ההגדרה. נכון לעכשיו, הוא משמש רק כדי לקבוע אם להפעיל את Blockly.Events.fire באופן מיידי (הפעלה מיידית תתבצע כברירת מחדל).

sharedTestTeardown:
  • הפונקציה מנקה את סביבת העבודה this.workspace (בהתאם למיקום שבו היא הוגדרה, מידע נוסף זמין בקטע 'דרישות הבדיקה').
  • שחזור של כל ה-stubs.
  • מחיקת כל סוגי הבלוק שנוספו באמצעות 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) ב-teardown של הסוויטה הפנימית הזו.

מבנה הבדיקה

בדיקות יחידה בדרך כלל בנויות לפי מבנה קבוע, שאפשר לסכם אותו כך: סידור, פעולה והצהרה.

  1. ארגון: מגדירים את המצב של העולם ואת כל התנאים הנדרשים להתנהגות שנבדקת.
  2. פעולה: קוראים לקוד שנבדק כדי להפעיל את ההתנהגות שנבדקת.
  3. Assert: הגשת טענות נכונות (assertions) לגבי הערך המוחזר או אינטראקציות עם אובייקטים מדומים כדי לוודא שנכונות.

בבדיקה פשוטה יכול להיות שלא תהיה התנהגות שצריך לארגן, ואפשר לשלב את שלבי הפעולה וההצהרה על ידי הטמעת הקריאה לקוד הנבדק בטענת הנכוֹנוּת (assertion). במקרים מורכבים יותר, כדאי לפעול לפי 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 נוספות.
  • לכל חבילת בדיקות ולבדיקה יש שם תיאורי.
  • טענות הנכוֹנוּת (assertions) של Chai משמשות להצהרות על הקוד.
    • אפשר לספק ארגומנט מחרוזת אופציונלי שיוצג אם הבדיקה תיכשל. כך קל יותר לנפות באגים בבדיקות לא תקינות.
    • סדר הפרמטרים הוא chai.assert.equal(actualValue, expectedValue, optionalMessage). אם מחליפים את actual ב-expected, הודעות השגיאה לא יהיו הגיוניות.
  • משתמשים ב-Sinon כדי ליצור סטאבים של שיטות כשלא רוצים לקרוא לקוד האמיתי. בדוגמה הזו לא רוצים לקרוא לפונקציית המדדים האמיתיים כי היא לא רלוונטית לבדיקה הזו. אנחנו מתעניינים רק באופן שבו השיטה שנבדקת משתמשת בתוצאות. ‏Sinon יוצר סטאבים לפונקציה getMetrics כדי להחזיר תשובה מוכנה מראש, שאנחנו יכולים לבדוק בקלות בטענות הנכוֹנוּת (assertions) של הבדיקות.
  • שיטות 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. בתפריט הנפתח, בוחרים את החלק הרלוונטי במערכת לבדיקה ולוחצים על 'טעינה'. ב-Workspace אמורים להופיע בלוקים.
  3. לוחצים על 'JavaScript'.
    מעתיקים ומריצים את הקוד שנוצר במסוף JavaScript. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  4. לוחצים על Python.
    מעתיקים ומריצים את הקוד שנוצר במַפְרֵש Python. אם הפלט מסתיים ב-OK, הבדיקה עברה.
  5. לוחצים על 'PHP'.
    מעתיקים את הקוד שנוצר ומריצים אותו במַפְרִיט PHP. אם הפלט מסתיים ב-'OK', הבדיקה עברה.
  6. לוחצים על 'Lua'.
    מעתיקים את הקוד שנוצר ומריצים אותו במַפְרִיט Lua. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  7. לוחצים על 'Dart'.
    מעתיקים את הקוד שנוצר ומריצים אותו במפריד Dart. אם הפלט מסתיים ב-OK, הבדיקה עברה.

עריכת בדיקות של מחולל בלוקים

  1. טוענים את tests/generators/index.html בדפדפן.
  2. בוחרים את החלק הרלוונטי של המערכת מהתפריט הנפתח ולוחצים על 'טעינה'. ב-Workspace אמורים להופיע בלוקים.
  3. מבצעים שינויים או הוספות בבלוק.
  4. לוחצים על 'XML'.
  5. מעתיקים את ה-XML שנוצר לקובץ המתאים ב-tests/generators/.