Sunday, May 7, 2017

My problem with the screenPlay Pattern


Like anyone who is even remotely interested in Selenium, I too heard about the new emerging "Screenplay Pattern" (you can read about it here, or watch this talk) and took some time to consider it. My first reaction was "I think it's a bit silly, writing all of this for no gain at all", then "It looks like more overhead and duplication than I currently have with page-objects". And then I forgot about it.
In Selenium Conf UK (2016) Antony Marcano gave a talk about it which I watched just since it was well presented, and I wanted to see if I could figure out why is there so much fuss around this pattern. It turns out that the screenplay pattern is simply branded incorrectly, and presented as a (complex) solution to a problem that should be solved in a simpler manner. Apart from that, though, it is a really useful tool to have in my utility belt, after fixing the fundamental flaw it has, of course.
To put things simply - The screenplay pattern is not a good substitution for decent page-objects and, in fact, should co-exist with them.
If you read my previous posts about "page-objects are not enough" (pt.1, pt.2) then you might have noticed that I advocate for a layered infrastructure for your automation. Personally, I see three layers that are really needed (Alex Schladebeck is splitting it to 5 distinct layers, which might be a good idea, depending on your context). The three layers I need are: Tests, Business actions (or "flows") and atomic actions.
The atomic actions are stuff like "click on the 'next' button" or "select all users from the database" (In Alex's model, there's a lower level layer which will do "click on something" or "run this SQL query" - a layer very important if you have multiple technologies to drive your actions).
However, atomic actions are not really helpful for your day to day test, so the second layer is created - the business actions. Here you'll find stuff such as "add a new user", "change password" and so on. Business actions are the place to put the context knowledge in - today logging in requires a username and password. Tomorrow it might require an one-time-password sent by SMS. the business action is the place to invoke the specific atomic actions that compose the actual action (Again, in Alex's model, this is split into two layers - internal and external. I think it's nice that she made this distinction, I don't think it's crucial in my context).
Finally, there's the tests themselves - They should be calling the flows layer, and even though some atomic actions might be used for assertions, as a rule of thumb - test methods should not be calling atomic actions themselves. When it get's to UI actions, this rule is more strict - your test should not contain any selenium code or any page-objects (the same is true for any sort of technology you might be using to drive your UI).

As I hope it's easy to notice - the screenplay pattern fits very nicely to that middle layer. Clear business actions that hide the actual steps taken to perform them. I can think of no reason not to include page objects within the screenplay actions, thus separating "how do I interact with an element" from "which elements should I be interacting with?"
This brings me to another point that was touched upon in Anthony's talk - Bloated page objects should not exist.
By separating the responsibility (the "which" from the "how") , most of this is solved - a page object should contain only the elements in it and a way to interact with each element. Perhaps we might go as far as aggregating filling up an entire form, but anything more complex than "login" should be dealt with in the flows layer. By doing this and keeping the page-objects representing small pieces of the UI , no large classes are created, and the flows layer (where the screenplay pattern is really helpful) remains clean and without unnecessary duplication when two actions go through the same pages.


כמו כל אחד שמתעניין קצת בעולם של סלניום, גם לי יצא לשמוע על הדבר הזה שנקרא screenplay pattern (אפשר לקרוא על זה כאן או לצפות בהרצאה הזו) ועצרתי לחשוב על זה. תכל'ס? התגובה הראשונה שלי נעה בין משיכה בכתפיים לתהייה בשביל מה צריך את כל התסבוכת הזו - נראה שבמימוש סטנדרטי של התבנית הזו אני אסיים עם שכפול קוד משמעותי יותר מאשר יש לי כרגע עם page-objects. נו, מילא. קראתי, סיווגתי, המשכתי הלאה. 
בשנה שעברה, בכנס סלניום, Antony Marcano העביר הרצאה על התבנית הזו (נו, אותה הרצאה שקישרתי אליה שם למעלה), וצפיתי בה, בעיקר כי הוא העביר אותה היטב, אבל גם כי רציתי לראות - אולי אצליח להבין מה ההתלהבות הגדולה מסביב לזה. 
ואכן הבנתי משהו - כל סיפור התסריטים הזה (בפוסט הזה, לפחות, אשתמש ב"תסריט" במקום screenplay, כי זה נוח יותר להישאר בעברית) פשוט משווק לא נכון ולכן מציג פתרון מסובך לבעיה שעדיף לפתור באופן פשוט יותר. אבל חוץ מזה? נראה שהתסריטים הם משהו שכדאי להכניס לארגז הכלים שלי - אחרי שמתקנים את הפגם היסודי שבהם. 
בפשטות - התסריטים אינם חלופה טובה לpage objects בנויים כהלכה, וקל וחומר שאינם "הדור הבא" שלהם. למעשה, התסריטים צריכים לדור איתם בכפיפה אחת. 
אם יצא לכם לקרוא את שתי הרשומות שלי על page objects are not enough (חלק א', חלק ב'), בטח שמתם לב שאני ממליץ על אוטומציה שבנוייה בשכבות. בפרט, אני נוטה לחשוב על שלוש שכבות נפרדות (Alex Schaldebeck מציעה חלוקה לחמש שכבות שונות, מה שעשוי להתאים לפרוייקטים מסויימים). שלוש השכבות שלי הן שכבת הבדיקות, שכבת הפעולות העסקיות ושכבת הפעולות האטומיות. 
הפעולות האטומיות הן פשוטות - לחיצה על כפתור "הבא", שליפת מספר המשתמשים ממסד הנתונים. דברים כאלה (אצל אלכס יש שכבה בסיסית יותר בה יופיעו "לחיצה על אלמנט" או "הרצת שאילתה מול מסד הנתונים" - שזו שכבה חשובה מאוד אם יש לכם כמה דרכים לבצע את אותן פעולות אטומיות בסביבות שונות). 
עם זאת, פעולות אטומיות לא ייקחו אותנו רחוק מאוד - ולכן נולדה השכבה השנייה - שכבת הפעולות העסקיות. לפונקציות כאן כבר יש משמעות בעיני המשתמש. זה לא "לחץ כאן" "מלא את השדה הזה" אלא "הוסף משתמש חדש" או "החלף סיסמה". הפעולות העסקיות הן החלק בו נשים את הידע המוצרי - איך עושים דברים. הרי היום כניסה למערכת דורשת שם משתמש וסיסמה, אבל אולי מחר הכניסה תתבצע בעזרת סיסמה חד פעמית שתישלח במסרון. הפעולות העסקיות הן המקום ממנו נקראות הפעולות האטומיות, והן המקום בו מוגדר מה הפעולות האטומיות שצריך לבצע כדי להשיג תוצאה ספציפית (שוב, במודל של אלכס זה מפורט יותר ומחולק לשתי שכבות - חיצונית ופנימית. זו אבחנה שכדאי לעשות, אבל אני חושב שהיא פחות קריטית ברמה העקרונית). 
לסיום ישנה שכבת הבדיקות - כאן נכתבות הבדיקות בעזרת הפעולות העסקיות. למרות שניתן מדי פעם לקרוא לפעולות אטומיות לצורכי אימות (assertion), כלל האצבע הוא שהבדיקות קוראות רק לפעולות עסקיות.  כאשר מדברים על אוטומציה של UI, הכלל הזה צריך להיאכף בקפידה  - בבדיקות לא אמור להיות קוד של סלניום, או של page objects (והדבר נכון לכל טכנולוגיה בה אתם משתמשים כדי לגרום לממשק הגרפי לזוז). 

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

No comments:

Post a Comment