בדיקות יחידה

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

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

בדיקות JS

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

מריץ בדיקות

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

מבחני כתיבה

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

עוזרי בדיקה

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

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

sharedTestSetup:
  • מגדירה טיימרים מזויפים של sinon (בחלק מהבדיקות צריך להשתמש ב-this.clock.runAll).
  • Stubs Blockly.Events.fire להפעלה מיידית (ניתן להגדרה).
  • מגדירה ניקוי אוטומטי של קובצי blockType שהוגדרו ב-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. Arrange: מגדירים את המצב של העולם ואת כל התנאים הנחוצים להתנהגות שנבדקת.
  2. פעולה: מפעילים את הקוד שנבדק כדי להפעיל את ההתנהגות שבודקים.
  3. assert: טענות לגבי הערך המוחזר או האינטראקציות עם אובייקטים מדומים, כדי לוודא את נכונות הנתונים.

בבדיקה פשוטה, יכול להיות שלא יהיה צריך לארגן את ההתנהגות, ותוכלו לשלב את שלבי הפעולה והצהרת הבעלות על ידי הטמעת הקריאה לקוד שנבדק בטענת הנכוֹנוּת (assertion). במקרים מורכבים יותר, הבדיקות יהיו ברורות יותר אם תקפידו על שלושת השלבים האלה.

הנה דוגמה לקובץ בדיקה (פשוט יותר מהאמת).

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 בכל חבילה צריכות לכלול רק הגדרה גנרית החלה על כל הבדיקות. אם הבדיקה של התנהגות מסוימת מסתמכת על תנאי מסוים, צריך לציין אותו בבירור בבדיקה הרלוונטית.

בדיקות לניפוי באגים

  • תוכלו לפתוח את הבדיקות בדפדפן ולהשתמש בכלים למפתחים כדי להגדיר נקודות עצירה (breakpoint) ולבדוק אם הבדיקות נכשלות באופן בלתי צפוי (או עוברים באופן לא צפוי!).
  • מגדירים את .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. מהתפריט הנפתח, בוחרים את החלק הרלוונטי של המערכת לבדיקה, ולוחצים על 'Load' (טעינה). החסימות אמורות להופיע בסביבת העבודה.
  3. לוחצים על JavaScript.
    מעתיקים ומריצים את הקוד שנוצר במסוף JavaScript. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  4. לוחצים על Python.
    מעתיקים ומריצים את הקוד שנוצר במתרגם של Python. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  5. לחץ על PHP.
    מעתיקים ומריצים את הקוד שנוצר בכלי לתרגום PHP. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  6. לוחצים על Lua.
    מעתיקים ומריצים את הקוד שנוצר בכלי תרגום ל-Lua. אם הפלט מסתיים ב-"OK", הבדיקה עברה.
  7. לוחצים על 'חץ'.
    מעתיקים ומריצים את הקוד שנוצר בכלי לתרגום Dart. אם הפלט מסתיים ב-"OK", הבדיקה עברה.

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

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