מהי בדיקה

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

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

דוגמאות להתנהגות באינטרנט שכדאי לבדוק:

  • לוודא שהתכונה של אתר פועלת כראוי כאשר לוחצים על לחצן.
  • אישור שפונקציה מורכבת מניבה את התוצאות הנכונות.
  • השלמת פעולה שמחייבת התחברות משתמש.
  • בדיקה שטופס מדווח כראוי על שגיאה כאשר מזינים נתונים שגויים.
  • הקפדה על כך שאפליקציית אינטרנט מורכבת תמשיך לפעול אם למשתמש יש רוחב פס נמוך מאוד או שהוא לא יהיה מחובר לאינטרנט.

בדיקה אוטומטית לעומת בדיקה ידנית

אפשר לבדוק את התוכנה בשתי דרכים כלליות: בדיקה אוטומטית ובדיקה ידנית.

בבדיקות ידניות מעורבים בני אדם שמפעילים תוכנות באופן ישיר, למשל טעינת אתר בדפדפן ואישור ההתנהגות הצפויה. קל ליצור או להגדיר בדיקות ידניות. לדוגמה, האם האתר שלכם נטען? האם תצליחו לבצע את הפעולות האלה? אבל כל העברה עולה כסף רב מאוד זמן. בני אדם הם יצירתיים מאוד, שיכולים לבצע סוג של בדיקה שנקרא בדיקות מחקר, אבל עדיין יכול להיות שלא נבחין בכשלים או בחוסר עקביות, במיוחד אם מבצעים את אותה משימה הרבה פעמים.

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

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

דוגמה ליסודות השימוש

מבחינתנו, כמפתחי אתרים שכותבים JavaScript או שפות קשורות, בדיקה אוטומטית ותמציתית יכולה להיות סקריפט כמו זה שאתה מפעיל כל יום, דרך ה-Node או על ידי טעינתו בדפדפן:

import { fibonacci } from "../src/math.js";

if (fibonacci(0) !== 0) {
  throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
  throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}

זוהי דוגמה מפושטת שמספקת את התובנות הבאות:

  • מדובר בבדיקה כי היא מריצה תוכנה מסוימת (הפונקציה Fibonacci)), ומטרתה לוודא שההתנהגות שלה פועלת באופן שבו היא התכוונה, על ידי בדיקת התוצאות ביחס לערכים הצפויים. אם ההתנהגות לא נכונה היא גורמת לשגיאה, ש-JavaScript מבטא בהשלכה של Error.

  • גם אם אתם מריצים את הסקריפט הזה באופן ידני בטרמינל או בדפדפן, זו עדיין בדיקה אוטומטית כי אפשר להריץ אותו שוב ושוב בלי שתצטרכו לבצע פעולות נפרדות. בדף הבא, שבו פועלות הבדיקות, מופיע מידע נוסף.

  • הבדיקה הזו לא משתמשת באף ספרייה, אבל מדובר ב-JavaScript שיכול לרוץ בכל מקום, אבל היא עדיין בדיקה. יש כלים רבים שיכולים לעזור בכתיבת בדיקות, כולל כלים שנלמד בהמשך הקורס, אבל הם עדיין עובדים על העיקרון הבסיסי של יצירת שגיאות במקרה שמשהו משתבש.

בדיקת ספריות בפועל

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

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

import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";

assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");

אפשר לשפר את הבדיקה עוד יותר על ידי הגדרת בדיקות עצמאיות, אם הן מקובצות (אופציונלי) ל-Suites. החבילה הבאה בודקת באופן עצמאי את פונקציית Fibonacci ואת הפונקציה Catalan:

import { fibonacci, catalan } from "../src/math.js";
import { assert, test, suite } from "a-made-up-testing-library";

suite("math tests", () => {
  test("fibonacci function", () => {
    assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
    assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
  });
  test("relationship between sequences", () => {
    const numberToCheck = 4;
    const fib = fibonacci(numberToCheck);
    const cat = catalan(numberToCheck);
    assert.isAbove(fib, cat);
  });
});

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

בדיקות עם שם ייחודי שימושיות למשימות הבאות, בין היתר:

  • קביעת מידת ההצלחה של בדיקה לאורך זמן.
  • הבלטת באג או תרחיש לפי שם כדי לבדוק בקלות רבה יותר אם הבעיה נפתרה.
  • הרצת בדיקות מסוימות בנפרד מאחרים, למשל דרך מסנן של glob.

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

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

קנה המידה של הבדיקות

דוגמאות הקוד בקטע הקודם מתארות בדיקת יחידה, כי הן בודקות חלקים קטנים מהתוכנה, ובדרך כלל מתמקדות בקובץ אחד, ובמקרה הזה, רק פלט מפונקציה אחת. המורכבות של הבדיקה גדלה כשחושבים על קוד מכמה קבצים, רכיבים או אפילו מערכות מחוברות שונות (לפעמים אין לכם שליטה, כמו שירות רשת או ההתנהגות של תלות חיצונית). לכן, בדרך כלל השמות של סוגי הבדיקות משתנים בהתאם להיקף או לקנה המידה שלהם.

לצד בדיקות יחידה, כמה דוגמאות לסוגי בדיקות אחרים כוללות בדיקת רכיבים, בדיקה חזותית ובדיקת שילוב. לאף אחד מהשמות האלה אין הגדרות קפדניות, ויכול להיות שיהיו להם משמעויות שונות בהתאם לבסיס הקוד, לכן כדאי להשתמש בהם כקו מנחה ולהעלות הגדרות שמתאימות לכם. לדוגמה, מהו רכיב שנמצא בבדיקה במערכת שלכם? אצל מפתחי תגובות, יכול להיות שרכיב התגובה ממופה באופן מילולי ל'רכיב תגובה', אבל יכול להיות שתהיה לו משמעות שונה למפתחים בהקשרים אחרים.

הסולם של בדיקה בודדת יכול להציב אותו בתוך קונספט שלפעמים נקרא 'פירמידת הבדיקה'. זו אפשרות מומלצת לגבי אופן הבדיקה של הבדיקה והאופן שבו היא פועלת.

פירמידת הבדיקות,
 עם בדיקות מקצה לקצה (E2E) בחלק העליון, בדיקות שילוב באמצע ובדיקות יחידה בחלק התחתון.
פירמידת הבדיקות.

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

הסוגים האלה יורחבו בסוגים של בדיקות אוטומטיות.

בחינת ההבנה

מהם הפרימיטיבים שמספקים רוב הספריות וה-frameworks לבדיקת?

שירות הרצה שמשתמש בספק שירותי ענן.
חלק מהמריצים מבוססי הדפדפן מציעים דרך למיקור חוץ של הבדיקות, אבל זו לא תכונה רגילה של ספריות בדיקה.
טענות שגורמות לחריגות אם הן לא מתממשות.
למרות ששגיאה מסוימת יכולה להיכשל בבדיקה, assert() והגרסאות שלה בדרך כלל נכללות בחיפוש, כי קל יותר לכתוב את הבדיקות.
דרך לסווג בדיקות לפי פירמידת הבדיקות.
אין ממש דרך סטנדרטית לעשות זאת. אפשר לתת תחילית לשמות של הבדיקות או למקם אותם בקבצים שונים, אבל החלוקה לקטגוריות אינה מובנית בתוך רוב המסגרות לבדיקת.
היכולת להגדיר בדיקות עצמאיות לפי פונקציה.
השיטה test() כלולה כמעט בכל המשתמשים שרוצים לבדוק. זה חשוב כי קוד הבדיקה לא פועל ברמה העליונה של הקובץ, וכך מאפשר להריץ הבדיקה להתייחס לכל מקרה בדיקה כיחידה עצמאית.