מבוא לבדיקה עם ספוק וגרובי

1. הקדמה

במאמר זה נסתכל על ספוק, מסגרת בדיקות גרוביות. בעיקר, Spock שואפת להיות אלטרנטיבה חזקה יותר למחסנית JUnit המסורתית, על ידי מינוף של תכונות Groovy.

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

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

2. תלות של Maven

לפני שנתחיל, בואו נוסיף את התלות שלנו ב- Maven:

 org.spockframework spock-core 1.0-groovy-2.4 test org.codehaus.groovy groovy-all 2.4.7 test 

הוספנו גם את ספוק וגם את גרובי כמו בכל ספרייה סטנדרטית. עם זאת, מכיוון ש- Groovy היא שפת JVM חדשה, עלינו לכלול את ה- gmavenplus תוסף על מנת להיות מסוגל לקמפל ולהפעיל אותו:

 org.codehaus.gmavenplus gmavenplus-plugin 1.5 קומפילציה testCompile 

כעת אנו מוכנים לכתוב את מבחן ה- Spock הראשון שלנו, שייכתב בקוד גרובי. שים לב שאנחנו משתמשים ב- Groovy ו- Spock רק למטרות בדיקה ולכן תלות אלה נבדקות.

3. מבנה מבחן ספוק

3.1. מפרט ותכונות

כשאנחנו כותבים את המבחנים שלנו בגרובי, עלינו להוסיף אותם ל- src / test / groovy ספריה, במקום src / test / java. בואו ניצור את הבדיקה הראשונה שלנו בספריה זו ונקרא לה שם Specification.groovy:

מחלקה FirstSpecification מרחיבה מפרט {}

שים לב שאנחנו מרחיבים את מִפרָט מִמְשָׁק. כל כיתת ספוק חייבת להרחיב זאת על מנת להעמיד לרשותה את המסגרת. זה עושה זאת שמאפשר לנו ליישם את הראשון שלנו תכונה:

def "אחד פלוס אחד צריך להיות שווה לשניים" () {צפוי: 1 + 1 == 2}

לפני שתסביר את הקוד, כדאי גם לציין שבספוק, מה שאנו מכנים תכונה הוא שם נרדף במקצת למה שאנו רואים כ מִבְחָן ב- JUnit. כך בכל פעם שאנחנו מתייחסים לא תכונה אנו מתכוונים למעשה לא מִבְחָן.

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

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

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

לבסוף, אנו מבינים שאין קביעות. הסיבה לכך היא שהקביעה משתמעת, עוברת כאשר ההצהרה שלנו שווה נָכוֹן ונכשל כשזה שווה שֶׁקֶר. שוב, נסקור את הטענות בפרטים נוספים בקרוב.

3.2. בלוקים

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

@Test הציבור בטל givenTwoAndTwo_whenAdding_thenResultIsFour () {// ניתן int תחילה = 2; int שניה = 4; // כאשר int התוצאה = 2 + 2; // ואז assertTrue (תוצאה == 4)}

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

  1. להכין (מוקצה על ידי נתון) - כאן אנו מבצעים את כל ההתקנות הדרושות לפני ביצוע בדיקה. זוהי חסימה משתמעת, כאשר קוד כלל אינו נמצא בשום גוש שהופך לחלק ממנה
  2. מתי - כאן אנו מספקים א גירוי למה שנבדק. במילים אחרות, היכן אנו מפעילים את השיטה שלנו בבדיקה
  3. לאחר מכן - כאן שייכות הטענות. בספוק, הערכות אלה הן קביעות בוליאניות פשוטות, אשר יוסקרו בהמשך
  4. לְצַפּוֹת - זו דרך לבצע את שלנו גירוי ו טַעֲנָה בתוך אותו בלוק. תלוי במה שנמצא יותר אקספרסיבי, אנו עשויים לבחור או לא להשתמש בלוק זה
  5. לנקות - כאן אנו מפרקים כל משאבי תלות בבדיקה שאחרת היו נשארים מאחור. לדוגמה, ייתכן שנרצה להסיר קבצים ממערכת הקבצים או להסיר נתוני בדיקה שנכתבו למסד נתונים

בואו ננסה ליישם את המבחן שלנו שוב, והפעם נעשה שימוש מלא בלוקים:

def "שניים ועוד שניים צריכים להיות שווים לארבע" () {נתון: int שמאל = 2 int ימין = 2 כאשר: int תוצאה = שמאל + ימין ואז: תוצאה == 4}

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

3.3. מינוף תכונות גרוב לטענות

בתוך ה לאחר מכן ו לְצַפּוֹת חסימות, קביעות הן מרומזות.

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

def "אמור להיות מסוגל להסיר מהרשימה" () {נתון: def list = [1, 2, 3, 4] מתי: list.remove (0) ואז: list == [2, 3, 4]}

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

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

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

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

כדאי גם להדגים מה קורה כאשר המבחן שלנו נכשל. בואו נגרום לו להישבר ואז נראה מה פלט למסוף:

התנאי לא מרוצה: רשימה == [1, 3, 4] | | | false [2, 3, 4] ב- FirstSpecification. אמור להיות מסוגל להסיר מהרשימה (FirstSpecification.groovy: 30)

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

3.4. קביעת חריגים

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

def "צריך להוציא אינדקס מחוץ לתחום בעת הסרת פריט שאינו קיים" () {נתון: def list = [1, 2, 3, 4] כאשר: list.remove (20) ואז: זרק (IndexOutOfBoundsException) רשימה. גודל () == 4}

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

4. בדיקות מונעות נתונים

4.1. מהי בדיקה מונעת נתונים?

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

4.2. יישום מבחן פרמטרי ב- Java

בהקשר כלשהו, ​​כדאי ליישם בדיקה פרמטרית באמצעות JUnit:

@RunWith (Parameterized.class) מחלקה ציבורית FibonacciTest {@Parameters נתונים אוספים סטטיים ציבוריים () {return Arrays.asList (אובייקט חדש [] [] {{1, 1}, {2, 4}, {3, 9}} ); } קלט אינטלי פרטי; צפוי אינטי פרטי; FibonacciTest ציבורי (קלט int, int צפוי) {this.input = קלט; זה.צפוי = צפוי; } מבחן הריק הציבורי @ @ מבחן () {assertEquals (fExpected, Math.pow (3, 2)); }}

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

4.3. שימוש בטאבלטים בספוק

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

def "מספרים בכוח של שניים" (int a, int b, int c) 4 3 

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

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

4.4. כאשר תאריך כישלון נכשל

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

התנאי לא מסופק: Math.pow (a, b) == c | | | | | 4.0 2 2 | 1 שקר צפוי: 1 בפועל: 4.0

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

5. ללעג

5.1. מה ללעג?

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

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

5.2. לגלוג באמצעות ספוק

לספוק יש מסגרת לעגית משלו, תוך שימוש במושגים מעניינים שהביאו ל- JVM על ידי גרובי. ראשית, בוא נקים א לִלְעוֹג:

PaymentGateway paymentGateway = מדומה ()

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

def תשלוםGateway = מדומה (PaymentGateway)

עכשיו, בכל פעם שאנחנו קוראים לשיטה שלנו PaymentGateway לִלְעוֹג, תגובת ברירת מחדל תינתן, מבלי שתופעל מופע אמיתי:

מתי: def result = paymentGateway.makePayment (12.99) ואז: result == false

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

5.3. שיטת הדקירה קוראת לועגים

אנו יכולים גם להגדיר שיטות שנקראות ללעג שלנו להגיב בצורה מסוימת לוויכוחים שונים. בואו ננסה להשיג את שלנו PaymentGateway ללעג לחזור נָכוֹן כאשר אנו משלמים תשלום של 20:

נתון: paymentGateway.makePayment (20) >> נכון כאשר: def result = paymentGateway.makePayment (20) ואז: result == true

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

עכשיו, בואו ננסה עוד כמה סוגים של דקירה.

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

paymentGateway.makePayment (_) >> נכון

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

paymentGateway.makePayment (_) >>> [נכון, נכון, שקר, נכון]

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

5.4. אימות

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

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

בואו ננסה לאמת ששיטה עם סוג החזר ריק נקראת:

def "צריך לאמת הודעה נקראה" () {given: def notifier = Mock (Notifier) ​​when: notifier.notify ('foo') then: 1 * notifier.notify ('foo')} 

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

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

2 * מודיע. התראה ('foo')

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

מעט מדי קריאות עבור: 2 * notifier. Notify ('foo') (קריאה אחת)

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

2 * מודיע. התראה (_)

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

2 * הודעה. הודע (! 'Foo')

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

6. מסקנה

במאמר זה, נתנו פרוסה מהירה דרך בדיקות עם Spock.

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

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

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


$config[zx-auto] not found$config[zx-overlay] not found