מיטוב מבחני שילוב האביב

1. הקדמה

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

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

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

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

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

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

2. מבחני אינטגרציה

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

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

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

מבחני אינטגרציה עוזרים לנו לבנות ביטחון אך יש להם מחיר:

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

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

3. בדיקת אפליקציות אינטרנט

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

  • MockMvc: לועג לממשק ה- API של servlet, שימושי לאפליקציות אינטרנט שאינן מגיבות
  • TestRestTemplate: ניתן להשתמש בהצביע על האפליקציה שלנו, שימושי לאפליקציות אינטרנט שאינן תגובתיות בהן רשתות לגלוגיות אינן רצויות
  • WebTestClient: הוא כלי בדיקה לאפליקציות אינטרנט תגובתיות, הן עם בקשות / תגובות לגלוגות או פגיעה בשרת אמיתי.

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

אל תהסס להעיף מבט אם תרצה להעמיק.

4. מיטוב זמן הביצוע

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

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

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

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

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

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

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

בואו נתחיל!

4.1. שימוש בחוכמה בפרופילים

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

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

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

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

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

ישנם כמה טריקים שנוכל לזכור כשמדובר בפרופילים במבחני שילוב:

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

4.2. הבעיות עם @MockBean

@MockBean הוא כלי די חזק.

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

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

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

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

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

4.3. ארגון מחדש @MockBean

בחלק זה ננסה לשקף מחדש מבחן 'איטי' באמצעות @MockBean כדי לגרום לו לעשות שימוש חוזר במטמון ApplicationContext.

נניח שאנחנו רוצים לבדוק POST שיוצר משתמש. אם היינו לועגים - משתמשים @MockBeanנוכל פשוט לוודא שהשירות שלנו נקרא באמצעות משתמש מסודר יפה.

אם בדקנו את השירות שלנו כראוי גישה זו צריכה להספיק:

class UsersControllerIntegrationTest: AbstractSpringIntegrationTest () {@Autowired lateinit var mvc: MockMvc @MockBean lateinit var userService: UserService @Test links links () {mvc.perform (post ("/ users") .contentType (MediaType.APPLICATION_JSON). "" {"name": "jose"} "" ") .andExpect (status (). isCreated) אמת (userService) .save (" jose ")}} ממשק UserService {fun save (name: String)}

אנחנו רוצים להימנע @MockBean אף על פי כן. אז בסופו של דבר נמשך את היישות (בהנחה שזה מה שהשירות עושה).

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

עם זאת, זה מפר את גבולות הבדיקה:

קישורים מהנים @Test () {mvc.perform (פוסט ("/ משתמשים") .contentType (MediaType.APPLICATION_JSON) .content ("" "{" name ":" jose "}" ")). AndExpect (status ) .isCreated) assertThat (JdbcTestUtils.countRowsInTable (jdbcTemplate, "משתמשים")) .isOne ()}

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

אם אנו מפעילים את האפליקציה שלנו באמצעות HTTP, האם נוכל לקבוע את התוצאה גם באמצעות HTTP?

קישורים מהנים @Test () {mvc.perform (פוסט ("/ משתמשים") .contentType (MediaType.APPLICATION_JSON) .content ("" "{" name ":" jose "}" ")). AndExpect (status ( ) .isCreated) mvc.perform (get ("/ users / jose")). andExpect (status (). isOk)}

ישנם כמה יתרונות אם נלך לפי הגישה האחרונה:

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

כמובן, זה לא תמיד אפשרי מסיבות שונות:

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

4.4. לחשוב בזהירות על @DirtiesContext

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

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

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

4.5. שימוש בפרוסות בדיקה

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

כמו כן, המסגרת תדאג להגדרת המינימום.

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

  • @JsonTest: רושמת רכיבים רלוונטיים ל- JSON
  • @ DataJpaTest: רושמת שעועית JPA, כולל ה- ORM הזמין
  • @JdbcTest: שימושי לבדיקות JDBC גולמיות, מטפל במקור הנתונים וב DBs בזיכרון ללא סלסולי ORM
  • @ DataMongoTest: מנסה לספק הגדרת בדיקת מונגו בזיכרון
  • @WebMvcTest: נתח בדיקת MVC מדומה ללא שאר האפליקציה
  • ... (אנחנו יכולים לבדוק את המקור כדי למצוא את כולם)

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

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

4.6. שימוש בירושה כיתתית

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

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

נוכל לקבוע שם את כל דרישות הבדיקה:

  • רץ האביב - או רצוי ששולט, למקרה שנזדקק לרצים אחרים בהמשך
  • פרופילים - באופן אידיאלי המצרף שלנו מִבְחָן פּרוֹפִיל
  • תצורה ראשונית - הגדרת מצב היישום שלנו

בואו נסתכל על מחלקת בסיס פשוטה המטפלת בנקודות הקודמות:

@SpringBootTest @ActiveProfiles ("test") מחלקה מופשטת AbstractSpringIntegrationTest {@Rule @JvmField val springMethodRule = SpringMethodRule () אובייקט נלווה {@ClassRule @JvmField val SPRING_CLASS_RULE = SpringClassRule ()}}

4.7. הנהלת המדינה

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

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

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

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

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

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

בדוגמה שלנו, נניח שיש מספר מאגרים (ממקורות נתונים שונים), ו- a Wiremock שרת:

@SpringBootTest @ActiveProfiles ("test") @ AutoConfigureWireMock (port = 8666) @ AutoConfigureMockMvc class מופשט AbstractSpringIntegrationTest {// ... כללי האביב מוגדרים כאן, דילגו לבהירות @ lateinit var wire מוגן באופן אוטומטי: WireMockServer: WireMockServer @ JdbcTemplate @Autowired lateinit var repos: הגדר @Autowired lateinit var cacheManager: CacheManager @Before fun resetState () {cleanAllDatabases () cleanAllCaches () resetWiremockStatus ()} fun cleanAllDatabases () {JdbcTestUtils.deleteFromTables (jdbcTemplate, "table1", "table1" ". table1 ALTER COLUMN id RESTART WITH 1 ") repos.forEach {it.deleteAll ()}} cleanAllCaches () {cacheManager.cacheNames .map {cacheManager.getCache (it)} .filterNotNull () .forEach {it.clear () }} reset resetWiremockStatus () {wireMockServer.resetAll () // הגדר בקשות ברירת מחדל אם יש}}

4.8. שיקום מחדש למבחני יחידות

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

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

דפוס אפשרי כאן כדי להשיג זאת בהצלחה יכול להיות:

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

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

5. סיכום

במאמר זה היה לנו מבוא למבחני אינטגרציה עם דגש על אביב.

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

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

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


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