מדריך סופי לבדיקת יחידות ביישומי React עם Jest ו-React-Testing

מבוא

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

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

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

  • עיבוד עצי רכיבים בסביבת בדיקה פשוטה והצהרות לגבי הביצועים שלהם
  • ריצה "בדיקות מקצה לקצה", הכולל בדיקת אפליקציה שלמה בסביבת דפדפן מציאותית

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

הערה: אתה יכול לקבל גישה למאגר עבור המדריך הזה ולשחק עם כל מה שיש בו, באמצעות זה קישור ב-GitHub.

מה זה בדיקה?

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

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

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

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

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

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

גישות לבדיקות

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

בדיקה ידנית

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

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

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

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

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

בדיקה אוטומטית

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

ישנן טכניקות רבות לכתיבת מבחנים אוטומטיים:

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

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

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

סוגי בדיקות

עד כה, הסתכלנו על מבחנים ברמה גבוהה. הגיע הזמן לדון בסוגי המבחנים השונים שניתן לכתוב.

ישנם שלושה סוגים של מבחני יישומים חזיתיים:

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

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

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

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

אם אתה רוצה להבין את היסודות של בדיקות מקצה לקצה עם Cypress - קרא את שלנו "בדיקה מקצה לקצה ב-JavaScript עם ברוש"!

יתרונות וחסרונות של בדיקה

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

יתרונות

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

חסרונות

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

סקירה כללית של בדיקת יחידות

עד כה, בדקנו את הבדיקות באופן כללי. עכשיו זה הזמן לצלול לכל מה שקשור לבדיקת יחידות וכיצד לכתוב בדיקות יחידות ביישומי React!

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

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

היתרונות של מבחני יחידה

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

חסרונות של בדיקות יחידה

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

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

יחידה בדיקת אפליקציית React - פרויקט הדגמה

בואו נסתכל על דוגמה אמיתית של יחידה לבדיקת אפליקציית React!

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

הגדרת פרויקט

השמיים create-react-app command, שנבנה על ידי צוות React, היא הדרך הטובה ביותר להתחיל ביצירת יישום React בעולם האמיתי ובקנה מידה גדול מכיוון שהוא מוכן לשימוש ועובד בקלות עם ספריית בדיקות צחוק. אם אתה פותח את package.json קובץ, תגלה שיש לנו תמיכה כברירת מחדל עבור יש ו ספריית בדיקות תגובה ב setupTests.js קוֹבֶץ. זה מבטל את הצורך שלנו להתקין באופן ידני את Jest בפרויקט שלנו אם נצטרך!

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

$ npx create-react-app react-unit-tests

אם כבר התקנת את הכלי, צור אפליקציית React ושמה לה react-unit-tests:

$ create-react-app react-unit-tests

הערה: npx משתמש בגרסה העדכנית ביותר של create-react-app, בעוד שגרסה המותקנת ברחבי העולם אולי לא. בדרך כלל מומלץ להפעיל את הכלי npx כדי להבטיח את הגירסאות העדכניות ביותר, אלא אם כן אתה רוצה להשתמש בגרסה אחרת בכוונה.

לאחר מכן, אנו נכנסים לספריית הפרויקט ומתחילים את שרת הפיתוח:

$ cd react-unit-tests && npm start
// OR
$ cd react-unit-tests && yarn start

זה יוציא את האפליקציה החדשה שנוצרה שלנו בדפדפן ב- localhost:3000.

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

בניית רכיב המונה

ב src ספריית הפרויקט שלנו, צור קובץ חדש בשם Counter.js. ב Counter.js, נגדיר את כל חלקי הרכיב. הוא יכיל פונקציות שונות של המונה, כולל increment(), decrement(), restart(), ו switchSign(), אשר הופך את ערך הספירה משלילי לחיובי בעת לחיצה. פונקציות אלה נוצרות לצורך מניפולציה של ערך הספירה הראשוני (שמועבר בתור אבזר):


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount((prev) => prev + 1);
  };

  const decrement = () => {
    setCount((prev) => prev - 1);
  };

  const restart = () => {
    setCount(0);
  };

  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
        Count: <h3>{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

לאחר מכן, עדכן App.js:


import "./App.css";
import Counter from "./Counter";

function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}

export default App;

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

מדריך סופי לבדיקת יחידות ביישומי React עם Jest ו-React-Testing Intelligence Data PlatoBlockchain. חיפוש אנכי. איי.

יצירת בדיקות לרכיבים

בואו ניצור קובץ בדיקה בשם Counter.test.js לייצג את הבדיקה עבור רכיב ה-Counter. הקפד למחוק גם App.test.js כך שהוא לא יוצר תוצאות לא רצויות בזמן שאנו מריצים בדיקות.

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

בנוסף, קבצי בדיקה ממוקמים בדרך כלל ב-a /test בספרייה במקביל עם זאת, לספריית השורש של קוד המקור שלך, זה גם תלוי בצוות.

In Counter.test.js, אנו מייבאים תחילה את Counter רכיב, ואז התחל את הבדיקה עם describe() פונקציה לתיאור כל הפונקציות השונות שיכולות להתרחש בתוך הרכיב.

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

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

בדיקת ערך המונה ההתחלתי

בעת בדיקה, זה עוזר ליצור רשימה שיטתית של תכונות והיבטים של תכונה נתונה - המצבים שבהם רכיבים יכולים להיות, מה יכול להשפיע עליהם וכו'.

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


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(screen.getByTestId("count").textContent).toEqual(0);
  });
});

כאן, השתמשנו ב- screen מופע מספריית React Testing כדי לעבד את הרכיב למטרות בדיקה. זה שימושי לעיבוד גרסה מדומה של רכיב שייבדק. ומאז ה

אלמנט שמחזיק את count הערך חייב להשתנות באופן דינמי, אנו משתמשים ב- screen.getByTestId() פונקציה להאזין לו ולהביא את הערך שלו עם textContent נכס.

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

הערה: השמיים screen הפונקציה מחזירה צומת DOM תואם עבור כל שאילתה או זורקת שגיאה אם ​​לא נמצא אלמנט.

ואז, ב Counter.js רכיב, נקשיב ל-

אלמנט בזמן בדיקה על ידי הגדרת a data-testid תכונה לאלמנט עם ערך count:


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  const increment = () => {
    setCount((prev) => prev + 1);
  };
  const decrement = () => {
    setCount((prev) => prev - 1);
  };
  const restart = () => {
    setCount(0);
  };
  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
      	
        Count: <h3 data-testid="count">{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

כדי לבדוק אם הראשונית count ערך שווה ל- 0, אנו משתמשים ב- expect() שיטה לתאר מה מצופה מהמבחן שקבענו! במקרה שלנו, אנו מצפים שערך הספירה הראשוני יהיה 0 אז אנחנו משתמשים ב toEqual() שיטה, המשמשת כדי לקבוע אם הערכים של שני אובייקטים מתאימים. במקום לקבוע את זהות האובייקט, ה toEqual() matcher בודק באופן רקורסיבי את כל השדות עבור שוויון.

הערה: הבדיקה מתמקדת במיוחד במידע שאתה מציג; בדוגמה שלנו, כלומר, ה Counter רכיב שקיבל an initialCount לִתְמוֹך. זה מצביע על כך שגם אם קובץ אחר - נגיד, בואו App.js— חסרים אביזרים ב Counter רכיב, המבחן עדיין יעבור כי הוא מתמקד אך ורק בו Counter.js ולא יודע איך להשתמש ב Counter רְכִיב. בנוסף, מכיוון שהבדיקות אינן תלויות זו בזו, עיבוד אותו רכיב עם אביזרים שונים במבחנים אחרים לא ישפיע גם כן.

כעת, נוכל להריץ את מבחן הסט:

$ yarn test

המבחן אמור להיכשל:

FAIL  src/Counter.test.js
  Counter
    × counter displays correct initial count (75 ms)

  ● Counter › counter displays correct initial count

    expect(received).toEqual(expected) // deep equality

    Expected: 0
    Received: "0"

       5 |   it("counter displays correct initial count", () => {
       6 |     render();
    >  7 |     expect(screen.getByTestId("count").textContent).toEqual(0);
         |                                                     ^
       8 |   });
       9 | });
      10 |

      at Object. (src/Counter.test.js:7:53)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.929 s, estimated 2 s
Ran all test suites related to changed files.

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


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
     
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });
});

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

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.271 s
Ran all test suites related to changed files.

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

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

בדיקת לחצן ההגדלה

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

מכיוון שהערך של הכפתור אינו דינמי, כלומר תמיד יהיה לו את הערך Increment בתוכו, אנו משתמשים ב- getByRole() שיטה במקום getByTestId() כדי לשאול את ה-DOM.

בעת שימוש getByRole() שיטה, תפקיד מתאר אלמנט HTML.

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

הדבר הבא לעשות הוא לדמות אירוע קליק באמצעות ה fireEvent() שיטה, המאפשרת לירות אירועים המדמים פעולות משתמש תוך כדי בדיקה.

ראשית, אנו כותבים מבחן כדי לראות אם ערך הספירה גדל ב-1 מערכו ההתחלתי של 0:


import { fireEvent, render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });

  it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue = Number(screen.getByTestId("count").textContent);
    expect(countValue).toEqual(1);
  });
});

זו התוצאה:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (66 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.405 s
Ran all test suites related to changed files.

לאחר מכן, נוכל גם לכתוב מבחן כדי לבדוק אם ה count הערך היה 0 לפני לחיצה על הכפתור על ידי הגדרת שניים expect() שיטות - אחת לפני הפעלת אירוע הקליק ואחרת לאחר הפעלת אירוע הקליק:


it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    let countValue1 = Number(screen.getByTestId("count").textContent);
    expect(countValue1).toEqual(0);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue2 = Number(screen.getByTestId("count").textContent);
    expect(countValue2).toEqual(1);
});

המבחנים עדיין עברו:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (82 ms)
    √ count should increment by 1 if increment button is clicked (60 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.388 s
Ran all test suites related to changed files.

בדיקת כפתור ההפחתה

באותו אופן כתבנו את המבחן עבור ה Increment כפתור, אנו מגדירים את הבדיקה עבור Decrement כפתור כזה:


it("count should decrement by 1 if decrement button is clicked", () => {
  render(<Counter initialCount={0} />);
  fireEvent.click(screen.getByRole("button", { name: "Decrement" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(-1);
});

זו התוצאה:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (73 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.346 s
Ran all test suites related to changed files.

בדיקת לחצן ההפעלה מחדש

בדומה ל Increment ו Decrement כפתורים, אנו מגדירים את הבדיקה עבור Restart כפתור כזה:


it("count should reset to 0 if restart button is clicked", () => {
  render(<Counter initialCount={50} />);
  fireEvent.click(screen.getByRole("button", { name: "Restart" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(0);
});

לצורך הבדיקה, הערך ההתחלתי נקבע ל-50 (ערך שרירותי), וכאשר הבדיקה מופעלת, כל ארבעת הבדיקות עוברות בהצלחה:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)
    √ count should increment by 1 if increment button is clicked (57 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (16 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.583 s
Ran all test suites related to changed files.

בדיקת כפתור שלט מתג

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


it("count invert signs if switch signs button is clicked", () => {
  render(<Counter initialCount={50} />);
  let countValue1 = Number(screen.getByTestId("count").textContent);
  expect(countValue1).toEqual(50);
  fireEvent.click(screen.getByRole("button", { name: "Switch signs" }));
  let countValue2 = Number(screen.getByTestId("count").textContent);
  expect(countValue2).toEqual(-50);
});

זו התוצאה:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (91 ms)
    √ count should increment by 1 if increment button is clicked (72 ms)
    √ count should decrement by 1 if increment button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (19 ms)
    √ count invert signs if switch signs button is clicked (14 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        3.104 s
Ran all test suites related to changed files.

וואו! כל הבדיקות עברו בהצלחה עבור אפליקציית הדלפק שלנו.

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

באופן כללי, נקודת התחלה טובה לבדיקה היא:

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

שיטות עבודה מומלצות לבדיקת יחידות

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

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

test('the success modal is visible', () => {});
test('the success modal has a success message', () => {});

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

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

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

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

סיכום

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

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

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

  • getByRole() - הוא מבצע שאילתות על אלמנט תוך שהוא מבטיח שהוא נגיש עם התפקיד והטקסט הנכונים
  • getByLabelText() - זוהי שאילתה מצוינת לאינטראקציה עם רכיבי טופס, היא גם בודקת שהתוויות שלנו מקושרות כהלכה לקלט שלנו באמצעות התכונות for ו-id
  • getByText() – כאשר אף אחת משתי השאילתות הקודמות אינה זמינה, ה- getByText() השיטה תהיה שימושית בגישה לאלמנטים המבוססים על טקסט הגלוי למשתמש
  • getByPlaceholderText(): שאילתה זו עדיפה מאוד על מזהה בדיקה כאשר כל מה שאתה צריך לשאול אלמנט הוא מציין מיקום.

אנו מקווים שהמדריך הזה יעזור לך! אתה יכול לקבל גישה למאגר עבור המדריך הזה ולשחק עם כל מה שיש בו, באמצעות זה קישור ב-GitHub.

בול זמן:

עוד מ Stackabuse