נראה לי שאין בשלב זה עוד מה לפרט על שיטות ליצור Proxies. בהמשך כשנדבר על Interception נדבר על שיטות Interception שמרחיבות את העיסוק.
יש עוד שימוש נפלא בProxies שטרם ציינתי. השימוש הוא ביצירת Mockים.
למי שלא מכיר, Mockים הם אובייקטים שאנחנו בד”כ משתמשים בהם בUnit Testים על מנת לדמות התנהגות של תלות חיצונית.
למשל, נניח שיש לנו מחלקה שאמורה להתחבר לDatabase ולשלוף טבלאות. את החיבור לDatabase היא מבצעת באמצעות איזשהו ממשק, למשל
|
|
כעת אנחנו מעוניינים לבדוק את המחלקה שמשתמשת בממשק הזה.
בUnit Test לא נרצה להתחבר לDatabase, מכמה סיבות:
- זו פעולה שלוקחת זמן
- זו פעולה שיכולה להכשל
לכן אם העיצוב של הקוד שלנו מספיק טוב, נוכל לבצע את הטריק הבא:
נדחוף לInstance של המחלקה שלנו איזשהו Instance של מחלקה שמממשת IDataProvider שנממש אנו, עם התנהגות משלנו – כלומר במחלקה זו לא יהיה חיבור לDatabase, אלא היא תמומש בצורה אחרת, למשל תחזיר תמיד אותו ערך, או מימוש אחר.
למחלקה שנממש קוראים בד"כ Mock. כאמור Mockים משמשים בעיקר בUnit Testים.
עד כה דיברנו על מה זה Mock באופן כללי.
עכשיו לבעיה: מה קורה אם יש לנו הרבה ממשקים שאנחנו צריכים לעשות להם Mock? מה אם יש להם הרבה פונקציות ואנחנו לא מעוניינים לממש את כולן, אלא רק את חלקן?
יוצא שאנחנו כותבים הרבה מאוד Mockים, ויכול להיות שMock מסוים שהשקענו בו הרבה עבודה משמש אותנו בUnit Test בודד.
כאן Proxyים נכנסים לתמונה:
מה שאנחנו יכולים לעשות זה ליצור Proxy שמה שהוא עושה זה קורא לפונקציה ידועה שמה שהיא עושה זה ניגשת לאיזשהו מבנה נתונים עם המידע על הפונקציה שנקראה והפרמטרים איתם נקראה, וממנו מקבלת איזשהו ערך – זה יהיה ערך ההחזר של הפונקציה.
הדבר הזה מאפשר יכולות נחמדות, כמו למשל ליצור Mock שמחזיר ערך ידוע עבור קריאה לפונקציה עם פרמטרים ידועים.
למעשה, אין צורך להמציא את הגלגל – יש Frameworkים מצוינים (שאפילו מתבססים על DynamicProxy) שמאפשרים את היכולת הזאת.
אחד הFrameworkים האלה הוא Moq המאפשר לנו לעשות את זה באמצעות Expression Trees של Framework 3.5 (ראו גם טיפים 173-181)
למשל דוגמה ליצירת Mock:
|
|
כעת נוכל לקבל אובייקט שמממש IFoo ע"י:
|
|
בקיצור, Moq מומלץ בחום לכל מי שנאלץ לכתוב Unit Testים 😃
לזכור ולא לשכוח.