מפרט כתיבה עם Kotlin ו- Spek

1. הקדמה

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

במדריך זה נציג את מסגרת Spek - מסגרת בדיקת מפרט עבור Java ו- Kotlin.

2. מהי בדיקת מפרט?

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

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

מסגרות בדיקת מפרט ידועות נפוצות כוללות ספוק, מלפפון, יסמין ו- RSpec.

2.1. מה זה ספק?

Spek היא מסגרת בדיקת מפרט מבוססת קוטלין עבור ה- JVM. הוא נועד לעבוד כמנוע בדיקה JUnit 5. משמעות הדבר היא שנוכל לחבר אותו בקלות לכל פרויקט שכבר משתמש ב- JUnit 5 כדי להפעיל אותו לצד כל בדיקות אחרות שיש לנו.

אפשר גם להריץ את הבדיקות באמצעות מסגרת ה- JUnit 4 הישנה יותר, על ידי שימוש בתלות JUnit Platform Runner במידת הצורך.

2.2. תלות Maven

כדי להשתמש ב- Spek, עלינו להוסיף את התלות הנדרשת לבניית Maven שלנו:

 מבחן org.jetbrains.spek spek-api 1.1.5 org.jetbrains.spek spek-junit-platform-engine 1.1.5 test 

ה spek-api תלות היא ממשק ה- API המשמש למסגרת הבדיקה. זה מגדיר את כל מה שהבדיקות שלנו יעבדו איתו. ה spek-junit-platform-engine התלות היא אז מנוע הבדיקה JUnit 5 הדרוש לביצוע הבדיקות שלנו.

שים לב שכל תלות ה- Spek צריכות להיות באותה גרסה זו לזו. הגרסה האחרונה תוכל למצוא כאן.

2.3. מבחן ראשון

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

Spek דורש שהמבחנים שלנו יירשו ממעמד-על מתאים - בדרך כלל ספק - וכי אנו מיישמים את המבחנים שלנו על ידי העברת בלוק לבנאי הכיתה הזו:

מחלקה FirstSpec: Spek ({// יישם את הבדיקה כאן})

3. בדוק סגנונות

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

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

3.1. נָתוּן/עַל/זה

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

זה משתמש בשיטות הנקראות נָתוּן, עַל ו זה, המקונן במבנה זה, כדי לכתוב את המבחנים שלנו:

  • נָתוּן - קובע את התנאים הראשוניים לבדיקה
  • עַל - לבצע את פעולת הבדיקה
  • זה - לקבוע שפעולת הבדיקה בוצעה כהלכה

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

class CalculatorTest: Spek ({given ("A calculator") {val calculator = Calculator () on ("Adding 3 and 5") {val result = calculator.add (3, 5) it ("Produces 8") {assertEquals (8, תוצאה)}}}})

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

3.2. לְתַאֵר/זה

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

במקרה זה, אנו יכולים לקנן את לְתַאֵר שיטות ככל שאנחנו צריכים לכתוב את המבחנים שלנו:

class CalculatorTest: Spek ({לתאר ("A מחשבון") {מחשבון val = מחשבון () לתאר ("תוספת") {val תוצאה = calculator.add (3, 5) זה ("מייצר את התשובה הנכונה") {assertEquals ( 8, תוצאה)}}}})

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

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

3.3. סגנונות נוספים

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

הרשימה המלאה של מילות המפתח המקננות הזמינות היא:

  • נָתוּן
  • עַל
  • לְתַאֵר
  • הֶקשֵׁר

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

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

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

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

class DataDrivenTest: Spek ({תאר ("מבחן מונחה נתונים") {mapOf ("שלום" ל- "HELLO", "world" ל- "WORLD"). עבור כל {קלט, צפוי -> תאר ("שימוש באותיות רישיות ב- $ input") {it ("מחזיר נכון $ צפוי") {assertEquals (צפוי, input.toUpperCase ())}}}})

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

4. טענות

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

הבחירה הברורה תהיה org.junit.jupiter.api.Asserstions בכיתה, מכיוון שאנחנו כבר משתמשים במסגרת JUnit 5 כרץ המבחן שלנו.

עם זאת, אנו יכולים גם להשתמש בכל ספריית קביעה אחרת שנרצה אם היא תשפר את המבחנים שלנו - למשל Kluent, Expekt או HamKrest.

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

לדוגמא, המבחן הנ"ל שנכתב מחדש באמצעות Kluent נקרא כ:

class CalculatorTest: Spek ({לתאר ("A מחשבון") {מחשבון val = מחשבון () לתאר ("תוספת") {val תוצאה = calculator.add (3, 5) זה ("מייצר את התשובה הנכונה") {result shouldEqual 8}}}})

5. מטפלים לפני / אחרי

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

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

האפשרויות כאן הן:

  • לפני קבוצה
  • afterGroup
  • beforeEachTest
  • afterEachTest

אלה יכולים להיות ממוקמים בכל אחת ממילות המפתח המקננות ויחולו על כל מה שנמצא בקבוצה זו.

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

בעבודה מבחוץ, ספק יבצע כל אחד מהם beforeEachTest לחסום מיד לפני כל זה לחסום מקונן בתוך אותה קבוצה, וכל אחת מהן afterEachTest לחסום מיד אחרי כל זה לַחסוֹם. באותה מידה, Spek יבצע כל אחד מהם לפני קבוצה חסום מיד לפני כל קבוצה וכל אחת מהן afterGroup חסום מיד אחרי כל קבוצה בקינון הנוכחי.

זה מסובך ומומלץ להסביר אותו בדוגמה:

class GroupTest5: Spek ({תאר ("קבוצה חיצונית") {beforeEachTest {System.out.println ("BeforeEachTest 0")} לפני Group {System.out.println ("BeforeGroup 0")} afterEachTest {System.out.println ( "AfterEachTest 0")} afterGroup {System.out.println ("AfterGroup 0")} לתאר ("קבוצה פנימית 1") {beforeEachTest {System.out.println ("BeforeEachTest 1")} לפני Group {System.out.println ("BeforeGroup 1")} afterEachTest {System.out.println ("AfterEachTest 1")} afterGroup {System.out.println ("AfterGroup 1")} זה ("Test 1") {System.out.println (" מבחן 1 ")}}}})

התפוקה של הפעלת האמור לעיל היא:

לפני קבוצה 0 לפני קבוצה 1 לפני כל מבחן 0 לפני כל מבחן 1 מבחן 1 אחרי כל מבחן 1 אחרי כל מבחן 0 אחרי קבוצה 1 אחרי קבוצה 0

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

אנו יכולים גם לראות כי כל ה לפני קבוצה בלוקים מבוצעים לפני כל beforeEachTest בלוקים וההפך עבור afterGroup / afterEachTest.

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

6. נבדקי נושאים

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

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

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

class CalculatorTest: SubjectSpek ({subject {Calculator ()} לתאר ("מחשבון") {לתאר ("Addition") {val result = subject.add (3, 5) זה ("מייצר את התשובה הנכונה") {assertEquals ( 8, תוצאה)}}}})

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

6.1. תלות Maven

כדי להשתמש בסיומת הנושא, עלינו להוסיף תלות לבניית Maven שלנו:

 מבחן org.jetbrains.spek spek-subject-extension 1.1.5 

7. סיכום

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

זה חשוב כדי לאפשר לכל העמיתים לתרום לבדיקת היישום כולו.

לבסוף, ניתן למצוא קטעי קוד כמו תמיד ב- GitHub.


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