The Screenplay Pattern - a SOLID alternative to Page Objects | Antony Marcano

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
on your at the fat can you put your hands up that means you can hear me brilliant thank you do you hate when people say if you're at the back can you hear me at the back because they can't hear you how are they how are they going to know how are you going to know so good afternoon is anyone enjoying selenium conference yeah I've been really inspired by a few things today for anyone who doesn't know me that's me I've done a few things I've been actually off the conference circuit for a while but I used to run this website called testing reflections for the older ones among you might have known that and have contributed to various books including some of the ones there and I came off the conference circuit for about three years mainly because my son decided to take up a very time-consuming hobby and decided to go semi-pro as a racing driver which left me with zero time other than earning money and taking him to racetracks and answering his phone calls and chauffeuring him everywhere but hopefully you'll see a little bit more than more from me in the near future probably because I've done three there's my third conference in about five days so now I have a bit more time anyway so I was really inspired by Simon's talk this morning he had a particular thing that he said can you remember what it was about coaches his opinion on code yeah yeah and I was thinking about that and I really wanted to incorporate it and I thought well what what does that really mean I mean that could mean anything and you know is it seriously hindered in testing sometimes yeah I me think in some testing teams we heard Simon's story about handing a tester who'd never done any programming before the responsibility of writing all the test automation code now that's not fair but so we need knowledge of programming in what we do and if we start out without that programming it's great that we have a community around us that we're willing to step in and help and I love that story that Simon told what else seriously hindered in translation so I'll be expressing the intent of what we're trying to achieve with our test it is that coming across clearly in our code and I want to try and answer that a little bit today and is it seriously hindered in trance formation so how easy is our code to refactor I came up with that today would you believe anyway a little word of what a little word of no slime I didn't give me a heads up a little word of warning though one of the things that I see often is when we're trying to automate testing and people talk about their testing pyramid and what I want to talk to you about today is for the kinds of tests where we are actually interacting through all the layers of the application so this is an old-fashioned calculator it's got the display it's got the keypad it's got the old ticker real where it prints out things then it's got another layer which handles the actual computation and that's all at the bottom and there are separate components that handle different aspects of the computation in this old-fashioned calculator and a lot of our software is componentized in this way and layered in this way and what I want to suggest that when you are writing these tests in to end and if you are looking at the pattern that I'm going to talk to you a bit about today I hope you're not trying to test every single possible scenario through every single layer of the product because generally speaking it's not practical to do that so I can point you back to the testing pyramid that came up earlier anyway so I want to talk to you about the screen play pattern but I just just want to put one more disclaimer and that is it's a way of separating any kind of interaction between your tests and the product that you're trying to interact with so it can be used for restful api as well as webdriver page objects and so on but today I really want to focus on page objects anyone here worked with page objects before I was expecting that no surprise there okay so let's just take really basic application web app in this case it's a to-do list manager so I can I can put some - dues in there - remember to walk the dog watch the car all of the new MacBook Pro or not so I'm not so sure at the moment given the price tag and just a very simple application so if we had a page object that page objects is generally sort of set up in such a way that we have the we want to interact with and the tasks that we might do on that page is that consistent with your understanding anyone disagree I'm seeing nodding heads that's good okay so in a simple way so let's go back in time to understand a little bit about where page objects came from so this guy called Simon Stewart who I saw walking in a minute ago wrote a blog post back in 2009 now what was happening at the time selenium and I think webdriver at the time as well people were starting to use tools like this and they were having all sorts of reliability problems with their tests you know Oh selenium is rubbish it doesn't work you know you get kind of feedback like that bug reports coming in saying oh it doesn't work in this way but what was actually happening is people were writing their tests in the way that wasn't sustainable or maintainable there was duplication in their tests so eventually this particular blog post had had to be written just to explain the problem isn't with the framework the problem is with how we were writing tests the code itself was not of a good enough quality to be maintainable and as a result people were having issues with how they were using selenium not with selenium itself so one of the things I'd love to tell you the original version of the story of how Simon explained to me what it was that motivated him to do this but I'd like to but we were in a pub and I'm pretty sure it was several beers later and your memory is way better than mine but basically I think he said words along the lines of this morning all the code is and we've got to stop it we had to stop it so I had to come up with something that was easily adoptable made sense and kept people out of trouble for as long as possible and that's where page objects came from now let's consider just you know your most very basic kind of page object example again ways of interacting with ways of finding elements and the tasks we might do on it now I went trolling around the internet this is one of the well not the whole internet just github and this is one of the examples that I found and if we zoom out it kind of ends up looking like this now I'm sure no one in this room has any page objects that look anything like that when you zoom out so there's a few people like okay I'm sure we've all written code like this at some point in our careers so don't feel don't feel judged in any way what's wrong with it well part of the reason it's not readable is because I had to zoom out so the writing is really small and sorry yeah so has anyone heard the term code smell before as a way of describing problems that we that we see in code or smell in the code depending on what your sensory preference is okay so if we were going to talk about code smell did anyone does anyone know like any other named sort of code smells or anti-patterns for code that that's obvious with hmm it's not obvious we can throw this duplicated but that's how I really there's a really good chance that there is duplication in there but the code smell relate relating to this is the thing that tells us that probably is duplication so we all know what it's called it's really easy it's called large class ok that's some Java code large class and when you have a large class what happens people think oh this is the right place to put the thing scroll Scroll scroll but can't find it add the new method and then you end up with a whole bunch of duplication people are laughing and who cuz you've done it right we all have at some point you know when you got pressure you make it happen so if you have that what do you do next what can you do you can split it yeah so a martin fowler's way of describing page objects is not that they are objects representing a page they are page objects as in objects on the page for example so that's one way we might split them up for example okay but there are some key principles that can really guide us in how to handle this particular problem and one of those is a set of object-oriented design principles called solids anyone heard of those yeah so they came from Michael feathers and Robert Martin and they talked a little bit about what are the sort of key obvious principles that we should really look for in our code that makes it more maintainable and they came up with this particular acronym and we haven't got time to go into this partly because I thought it was a 45-minute talk and I just found out was 35 so we're only going to look at these two specific examples the single responsibility principle and the open-closed principle now the first one's easy he wants to tell me that one class should only do one thing absolutely pretty straightforward yeah what about the open closed principle anyone know that one tougher one isn't it the open closed principle tells us that a class should be open for extension but closed for modification so what that essentially means that we should be able to extend the behavior of a product by writing a new class and adding it without changing any code that already works so if you want to add functionality and you find yourself changing a class to add that functionality that means that it doesn't satisfy the open closed principle but if you can write a new class without changing anything else and add it and it just works that's the open closed principle to take it to the extreme in theory if you have an application that's deployed let's say it's in Java you've got jars or in Ruby Jem's it should be possible to write a new java class or some a new ruby modular class and put that out in a new jar file or a new gem and the application should just have the new functionality as a result that's that's the extensible to the extreme so you shouldn't have to change anything that's already deployed now we're not going to take it to the extreme but we are going to listen to these two principles partly because the two people that sort of helped highlight them as valuable I respect and at least in terms of their code and various other reasons so so let's take a look at what that means in the real world so back to our app to do app simple things had to do is mark them as complete that sort of thing and we talked about a a page object that might represent that I don't want to have that all of the code here because no one will be able to read anything but again we've got how to find the page elements and how a user completes given tasks what was the first what was the s in solid single responsibility principle how many responsibilities can we see for interesting why per function quite possibly wow you just jobs ahead so there's at least two how we find stuff and how a user might what tasks a user might perform on that page but it actually might be even more there might be one responsibility for each of those individual tasks but Before we jump ahead so let's have a think about how we could break this page up this page objects up so we're all familiar with the term refactoring which means to change the internal design without changing the external behavior and there are some named refactoring steps that we can take one of them is extract class so we can extract a class for one responsibility and extract class for another responsibility but then we've got this dependency between these two so this one's always going to be looking in there all the time so but also maybe there are more than is more than one responsibilities when I look at this I'm thinking oh maybe you these is response as a responsibility in its own right and another thing that could lead me to think to start thinking about that is maybe this class is still quite large what what is a large class by the way what do you think is a large class I don't know I've got this colleague he's got this huge screen that he turns that way and he has this font size that we pretty much have to be a fighter pilot to be able to read so I'm not sure about scrolling is a great rule it used to work when we had like 80 characters wide and about you know hundred tall but not now so James Lewis one of the guys is quite famous for talking about micro-services he likes to say that a class should be no bigger than his head I don't think it's heads that big so I think that's reasonable I think we all use our heads as a rough guy I wouldn't say go up to the screen like this you know just the rough guide okay right so maybe it's still a large class so maybe we can consider some other refactorings and we can still do extract class but maybe we can group things differently maybe we can group things based on the kinds of things that we're doing so we're adding to dues we're maintaining the to do so this has got a couple of tasks in it but then this is kind of mixing responsibilities again so maybe I'm not so sure about that and again maybe this one ends up being a bit too big so there's another named refactoring that we can use which is replace method with method object yeah so where you extract a single method out to a single class has anyone taken that step before you have a single method behavior has anyone used at any API is that kind of get you to create a single class can anyone name one of those quite a well-known one anyone use Java hang on one person uses Java here really you are so lucky oh oh there's a figure for you okay okay so if you if you if you use j-unit and you've use hamcrest assertions where it goes assert that something is equal to the equal to method is creating an instance of a match though it's not actually matching anything and then what it does is it provides you an interface where the assert that calls a match matches safely method passing in the value so it's not actually doing anything other than creating an instance of the behavior but then you have to actually call the thing for it to have that behavior part of the reason that it does that is because at the time when it's written and still now really java didn't have functions as first-class types if you're lucky enough to be working with a language that has functions the first class types you probably kind of do this stuff all the time so I wouldn't worry about it too much but this is another way of extracting things out so there's one other problem though one other thing that was like really really kind of bugging me working with some page objects and that was a question about what I'll be modeling when we when we of writing a page object what domain are we modeling and all kind of clicked for me again back in 2007 and that's me in the top left there you can just kind of see a little bit of my old ponytail there before I cut my hair and there's a few other people in here they don't recognize anyone in this picture you can see it there's a few other people this was the first agile Alliance functional test tools workshop and that had Jim Shaw did the art of agile pepper Clark did the robot framework Ben Syme Oh Elizabeth Hendrick surgeon eat Andrea Germans are is exceeded test patterns Ward Cunningham being totally bored like totally met her taking a picture of the person taking the picture of us and the rest Jane up there so there's a whole whole group of us kind of got together because at the time we were a bit frustrated with like some of the tools that were about tooling that was available at the time and a guy called Kevin Lawrence who's there in the blue t-shirts at the back with their sunglasses on and he started talking about user centered design and it's used since design as well as other things this concept called task analysis is used and the task analysis you think about how someone gets something done by looking at the tasks they perform and how they perform them so when you're looking at this particularly in UCD you have roles so who's this for goals why are they here and what outcome do they hope to achieve tasks so what are they doing and actions how are they doing it that makes sense seems pretty straightforward an action would be something like click here type this into that field tap swipe those are actions think interactions if it helps the task is what you're doing so if I want if I needed someone to you know turn the volume up on the microphone for example don't actually do that if I want someone to turn the volume before the microphone I wouldn't say you know reach forward touch the volume knob turn 12 degrees to the right I wouldn't say that I'd say all could you turn it up a bit and that's it you say what they're doing not how and I think what happens in many of our tests when we're using many frameworks is we get so hung up on the how that we forget what the users trying to do and why they're trying to do it and I really found that this particular framework was quite useful that this particular model was quite useful to helping me think about it and that evening I hacked together a an example of the first thing that I now call screenplay and I'll explain why in a little while because it took my thinking away from the application the solution the implementation I wasn't I thought maybe I shouldn't be modeling the implementation maybe I should be modeling what the user is trying to achieve maybe I should be modeling what they're doing and how they're doing it and maybe that will get me thinking differently about how I write my tests so I've got an example where it kind of hooks into cucumber and I've got another one with J in it I'll just show you this so here's how that kind of maps in into the real world so for the roles who so if you if you if you have a persona or if you have a particular role that you're thinking about in this in this example it's a pet clinic receptionist goal why are they doing what they're doing so in this particular case we're talking about you know finding all pet owners by default when you search and the task of what are they doing so they're searching for owners with the search terms that's what they're doing how they do it will then involve them you know entering the search terms into the search terms text box clicking clicking the button and then waiting to see what happens next so there's that distinction between what someone's doing and how between the declarative the what and the imperative the hell so this kind of got me thinking also about well who would play a role any ideas what kind of what name would you give to somebody that plays a role an actor exactly and different actors have different skills or abilities in this particular case I decided to call this actor James and this is this example is using something called using the serenity framework serenity BDD they've actually implemented an interpretation of the screenplay pattern into into that framework which I'm actually used on clients site which works very well for us and James can have one or more abilities in this case he just has the ability to browse the web and all that's doing is just wrapping up webdriver so we talked about abstracting away the specific details of what's doing the interactions and that allows us to replace them things like that with factory methods that can just load up the appropriate implementation that we require the actors can perform tasks so James can attempt to add a to-do item on our to-do app such as buying milk and this is what I was saying this thing here is not actually doing anything yet it's creating an instance of the add a to-do item task configured with buy some milk so if you've used hamcrest you'll have seen this kind of way of writing code before and then what happens that the attempts to you then calls the perform as method and then visits the task and says all right now I'm going to follow the instructions in this task so the instructions would then be actions so this is the add a to-do item class that's pretty much it and in here the actual attempts to and now they can actually perform each of the specific interactions now this is an older slightly older example but you actually pass in a series of interactions so hitting return is actually a separate interaction rather than a chained one tried to with I heard someone say earlier today I'll trouble with fluent API is you always have to chain but you don't in fact some of the best fluent API is you don't chain you tend to be passing in instances of things or you pass in functions that can interact with each other does that make sense so far as anyone confused I don't got a question at this point make sense okay now the things that the actions interact with really at this point just become data just ways of finding things as a colleague of mine called Andy Palmer who has a really cool concept called robots handles and I think the talk is available from selenium conf India so I do encourage you to check that out where he talks about how you can actually infer the strategy for interacting with something so you just say you know I if you have an app that if you have an app that allows you to choose I don't know services from a spa you should be able to just say well I want to choose the deep tissue massage and you shouldn't have to say how you choose that you shouldn't have to say it's a radio button you shouldn't have to say if it's a drop-down this or a free text field we should be able to infer that from the application load the appropriate strategy for the interactions and then do it for the purposes of today I don't want to go into that because that takes a whole 35 40 minutes to explain on its own so I'm just going to show you just eight in this example we put that data it doesn't have to be there it could be anywhere it could be in a JSON file however you want to keep that data so these are just the things we interact with now the key difference instead of a few large classes what you do end up with is lots of smaller ones so if you're working with Java what you end up with is sort of neatly separated classes but with modern IDs they can pretty much go and find the stuff that you want you start typing it you can almost all right when I use IntelliJ sometimes I feel like it's reading my mind like I just reach for the keyboard it says oh you want to say this am i oh yeah okay yep yep yep yeah they practically writes the code for me and it works just as well with with separate separated classes as it does when you have the dot operator the dot operator in many ways constrains us it says here's all you can do whereas the tasks are well here's the kind of thing you can do now because of this what we end up doing is we end up modeling not the solution we've implemented but the thing that the user is trying to do and when you want when you identify something new that the user wants to do something the what what do they want to do that the task do I have to change any classes what would I do next just write a new one and drop it in hence the open closed principle and the great thing about that is I can still reuse other classes so if I have an adder to do item and I have the need to add more than one to-do item I can create to-do items class that can be configured with a list of to-do items and then it just iterates over calling that same task again as you would with a method but everything is then kept completely separate neat and then if you if any functionality changes you tend to be changing it in one place the other thing about this is that if your UI changes in some key way some fundamental way that it forces you to restructure your page objects as anyone kind of found themselves in that situation where you've had yeah have you was it really easy yeah easy oh I think it's even easier with this I'm gonna I'm going to suggest why but you could prove me wrong so when how many people have page objects that inherit from other page objects most of us ok so there's other great little snippet of insight someone gave me something some years ago which is that we favor composition over inheritance we have composition inheritance is no longer in your way so you don't have to worry about how what side effects you might have having inherited from another another class and then when you've identified duplication you can then avoid having to pull all of that duplication up into that parent class because the duplication just sits in these separate tasks and if the application does change in some fundamental way it will usually be how the user achieves a particular outcome which generally will mean you might have to change one or two tasks not the entire structure of the application because if I if I have a let's say I have a the to-do list and for whatever reason somebody says we it would be much better if the way you added to do is like a little wizard type thing for whatever reason as long as after I've added this to do it doesn't matter to me whether it loads underneath or it loads a new page to show me that to do or takes me somewhere else as long as when I try to interact with the next thing that I ask for it's there it doesn't matter whether it was on that page or another page what can happen is when things start moving around it's the same element but you're now moving it to a different page or a different view or a different widgets if you're lucky enough to feel to be working in teams where you have influence over what the IDS are on these things then the ID can stay the same it doesn't matter where it is so you have far less need to change the actual code when the UI changes and that for me is one of the main benefits but yet again going back to the open-closed principle the great thing is we just add a new task class now I gave this talk the other week and I added this slide in sorry I had I added the cucumber slides in because doing this at the VDD exchange but you can also do this with without cucumber you can use JUnit or any xunit framework depending on what language you're working with the same principles apply but I was interested in seeing a way of doing this with meta programming in Ruby if you look on the river Glide github there's an old example of it called Keuka salad which shows you how to kind of do this with a quite a nice little DSL in this particular example it's still again a fluent API there given that when then are just sugar methods around James they just give you back and back the same actor that you passed it it's just some people like to write that in in some of their tests some people don't you don't have to do that you can just say James attempts to and then give it a list of tasks now there is one special kind of task and that's a question and this one here is a question because it can tell us something that we want to know so if we want to know what items were displayed we ask that question and in this particular implementation we're using hamcrest matches to see if the items displayed has the item milk but that's just a regular out-of-the-box hamcrest matcher some people saw the wording of some of these method names but I'll let you know that they've been very very carefully thought through so that the names of the tasks can always be in the present tense so you don't have to have James has started with an empty to do lists but then if you want to start with an empty to-do list or added to-do item you don't have to say James has added an empty to-do item you just say a James was able to add it to dual item or you can say James attempt to add a to-do item so that's pretty much it and the last piece of the puzzle for me was actually earlier this year when I was working with some colleagues of mine who were doing the implementation in serenity and with my colleague Andy Palmer and we were talking about what is what is this because some a friend of ours said I'll just call it the journey Pat and it's like talking about the journey of the actor and I thought oh there's something missing there and we were talking about it well the scenario is like a narrative almost like a script and it's for a cast of actors because we keep talking about and because we have separate actors we can support multiple actors so James can do something Nicole could do something Franzi can do something and Jim can do something you can have many different actors each doing different things independently of each other and each one ends up being a wrapper around their own instance of a browser for example so we thought about each actor can then be playing a different role and each actor has the task of performing actions to the best of their abilities and that's where we kind of came up with the screenplay pattern word of warning if you're thinking about trying this if any of you have watched this and thought oh actually that looks all right I think I'm going to use that please don't not straightaway anytime you come to a conference is one thing I'll always tell you and I learned this the hard way if you haven't learned it yourself anytime you go to a conference to see a great idea always do an experiment first see if it helps see what you learn from doing it try to don't try to aim to refactor everything in this way and in fact the refactoring steps that I gave you at the beginning were more about trying to help you refactor you're thinking about page objects not actually that's your page objects instead what I'd say at the start just consider writing something completely clean and fresh that interacts with the application that you already have something that you know you're familiar with just do that as experiment hopefully some of you will have time where you can do experiments and then see what you learn see how it feels and then if you think yes this is what this is better this is better for what we're trying to do then what you can start looking at is okay for any new test that we write we'll try taking this approach separately and bit by bit you might find that you'll be able to gradually move over from your old way of doing things to the new and I'll give you that piece of advice in anything that you learn at any conference about anything so I'm not saying don't use it I'm saying try it if you like it keep learning more and then keep it if it works for you so in summary so any code that we have the supports our tests if it is getting too much to handle sometimes I'd ask a bit like that calculator example are we testing too much through every layer of the application should we really be testing this many variations through every layer of the stack if you think about it if I can plug some electrodes into the component on the calculator and fire in really really fast calculations and check the answers are all right once I put the keyboard on top do I have to do all of those tests again or do I just have to make sure that we have the main basic usage scenarios flowing through our app now of course that depends on how much uh visibility you have of the the unit testing that's being done or how much involvement you have but just do bear that in mind secondly the classes with interaction methods like page objects are very very useful as training wheels they're a place to start but when you do see them grow when you see inheritance hierarchies get deeper and deeper and the sizes of the classes get larger I do encourage you take a step back and if you haven't done so already do look at some of the rich materials that are out there are and object-oriented design or for using a functional language functional languages and have a think about how you can start separating that code out in a way that makes sense but do keep in mind that question are you modeling the implementation or the problem you're trying to solve because the thing with training wheels is we try to go faster they actually make us less stable they actually make it slow early we kind of wobble a little bit and it's only when we take the training wheels off that we can actually go as fast as we're capable of going and the screenplay pattern is a way of allowing us to ride a little bit faster because we've taken the training wheels off page objects off I hope you found that talk interesting thank you very much I only found out two minutes before that I had to shave ten minutes off my talk and I don't know how I did that but thank you very much for your help two minutes for questions any questions concerns compliments thank you if your if there are same page for elements being used then even then yes I just trying to move them to a different class the same page elements what I would do is say if you if you if you bind the elements to that page if that element moves to a different page in the application you then have to mirror that change in your tests which is means that you have to change things in two places so your it's like the testing equivalent of repeating yourself so why I'd say is if you can if you can express you need to add an abstraction that gives you a name versus the actual way you locate the element in the example I gave it happened to sit in a class as a constant it doesn't have to you can put those into a JSON file and just use our text-based arguments to then load up the correct locator or you can put them in constants or an enum or whatever whatever you like but wide says try to try to separate separate the the way the application looks in the real world from the structure that you have for it in your tests because what's more important is that you can do this thing unless what you're testing is the structure and that's a different thing but if you are testing the structure of a UI then usually that's much better done from within the the unit tests of that UI rather than testing can it do the things that my customer wants it to do can we can we make sure it doesn't do the things we they don't want it to do that those are that's the kind of level at which I'm kind of thinking about this that answer your question yeah yeah thank you any other questions there's a question at the over here with the chat with the gray hoodie is it a compliment no oh because if it wasn't I was going to say oh we're out of time I'm sorry hey it was great cam you're right and it was great - thank you Andy when you're using multiple actors in a given tests are there any pitfalls there does it get quite complicated and like when should you stop with the complexity there's the simple answer to that is keep it short keep it simple because if if you if your test is that complicated you're probably testing more than one thing and I'd aim to separate the tests out into the specific things that you're trying to prove and then look for ways of removing duplication by being able to reset the browser and reuse it without having to start all over again for example but in my experience most of my tests are most ten lines long anything more than that and I have to ask myself what am i testing how many things am i testing let's just separate out into the separate goals if we go back to the concept of having a goal for each scenario that's the name then it makes sense anyway we're out of time thank you very much I'm around for the rest of today and I hope to see you you
Info
Channel: SeleniumConf UK
Views: 10,991
Rating: undefined out of 5
Keywords:
Id: 8f8tdZBvAbI
Channel Id: undefined
Length: 38min 46sec (2326 seconds)
Published: Wed Nov 16 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.