 All right, there we go. Good morning. We definitely have stragglers out there, but you guys are all on time. So, forget them. They'll come in any ways and stand towards the back or file in whatever. So welcome, good morning. Still morning, 10.45. So let's get rolling here. I'm not gonna use the stage because it's like on the other side of the room, it's like very confusing. I realize I got up there. I was like, this screen's all the way over there though I can't even see. So I'm gonna rock around, walk around a little bit. Plus, sometimes stages are creaky. It just throws me off. I'm like, look at this, aren't. I'm like, oh, I just like lost my train of thoughts. So we're gonna stay on actual earth ground down here. Earth ground, second floor. All right, so, anyways, good morning. This is the BeHat talk. I'm not even gonna intro into BeHat, cause I'm gonna talk about it in a second. Other than I will say BeHat is probably my favorite tool that I get to use in PHP. If someone made me stop using it, I would just retire. I'm like, that's fine. I had a good run. Y'all have good luck. I'm just gonna go do something else. So it's that great. I know this works from over this far. Hold on, hold on. I did, some of you saw me actually try this and it did work. There we go. Uh-uh, uh-uh, traveling, traveling. Okay, good. All right, awesome. My name is Ryan. Hello, everybody. I come from the symphony world. I'm the lead of the symphony documentation team. So when you look at the symphony documentation, a lot of that is my doing for better or worse. We have a lot of documentation. So I get a lot of compliments on it, but wouldn't you compliment the guy who wrote it? And if you didn't like it, you would just be like, oh, I'm gonna go over here instead of saying something mean to him. So actually, one time I was at a conference and I sat down at breakfast with some guys and I was like, oh, what do you guys do? And they're like, oh, use symphony. I was like, oh, really? Couldn't help myself. What do you think about the documentation? They're like, it's terrible. I was like, I gotta go. No, we have a lot of documentation, like a thousand pages. So it's all, well, ideally it's all good stuff. Anyways, my day job is I work for Camp University. It's PHP, symphony, screencast. We also have some Drupal related screencasts for the people that are going from the Drupal 7 to Drupal 8 kind of transition. If you wanna actually learn Drupal, which I'm guessing you guys are a little more expert level, you could go to Drupalize Me. And most importantly, there she is. I'm the husband. Oh, oh, I forgot to do that. Dang it. I did that really well yesterday. That's my costume that my lovely wife made me. That's Leanna back there, say hi Leanna. Hi Leanna. What a good group. So this is usually where I say something embarrassing about Leanna, I'll lie and say she's a great singer or I'll have you like high five afterwards. But today, I'm just gonna tell you that she is actually pregnant with our first child. Yeah. You know how like you don't tell people for a while? When she told me, and of course when you, like that's so early, you don't tell anybody when you first find out about it. Like we actually right now live with one of her bridesmaids in our house, so we have a roommate. And immediately she told me, I was like, we have to tell Beth, I wanna tell everybody. So I've been waiting four months so I can actually out her. So you guys are the first talk where I've actually outed her publicly. Yes. Okay. So we're gonna talk about a variety of things in this presentation. So let's see here, plan, work, miscommunicate, panic, put out fires, repeats. AKA project work. So we're gonna start with kind of the premise that when we work on things, actual projects, it just, a lot of times it just feels kind of chaotic. Now most people have probably seen these diagrams before. Can't remember what the website is, it says on the bottom, projectcartoon.com. They're really funny, there's a bunch of these. But anyways, how the customer explained it, how the project leader understood it, how the programmer wrote it, and what the customer really needed. So miscommunication. And my personal favorite, which is what the beta testers received. There's uncomfortable laughing because you guys know it's true. You're like, I don't know if it works. I didn't even refresh, but ship it off to the QA department for you guys. So yeah, so we are computer scientists, right? Computer scientists that wander into this cold gavel mess, and we all deal with it, and it's a difficult thing to solve. So we're gonna talk about some ways, systematic ways to solve this. In the beginning of this presentation is going to be a little bit more philosophical as far as strategies for planning these. And the second half is going to be super hands-on applying these things to actual code. So bear with me, but this is actually maybe even the harder parts, this beginning parts, because it's hard to kind of read online and get a feel for these things. So we have lots of different people coming to the process, they have different roles, and basically it comes down to miscommunication. We also, so that's kind of problem number one. Like your project manager, your developers, they have different skill sets, they communicate in different ways, and you're all trying to get on the same page. Problem number two is that your code and your business value made out of line, that's best summarized with this idea, which we've all done before, especially when you're younger. So yeah, we should just implement this feature because it's cool, or I'm gonna add bells and whistles where if you step back and think about it, you're like, you know that this doesn't really align with the business value. If you took this up to the owner of the company or whatever, they're gonna be like, I don't really care about that actually. So the third thing is just kind of like over planning, under planning, planning, you know, sometimes there's under planning and we all just like run the speed of light, don't really know what we're building. Other times you have like meeting after meeting after meeting and you're like beating the horse to death. So kind of getting that process sort of right. So this is what BDD behavior driven development aims to solve, aims to kind of give a system towards this process. And first thing, we've all heard of TDD, test driven development. So this is BDD, so where does that fit in? So once upon a time, somebody had the bright idea of writing unit tests and that was the best thing ever. That's amazing. We can auto test our code. Then somebody later said, well, we could actually write our tests first and then implement it. So that's test driven development, write the tests first. And then BDD is just kind of an evolution on that which says let's actually think about how we want it to work and then make it. And you'll see what I mean by that. That should just feel fluffy at this point. So Dan North, he's the Santa of behavior driven development. He said once behavior is a more useful word than test because if you talk to management and you say, oh, what we're gonna do is we're gonna write tests first and then we're going to write the feature afterwards. They're like, that sounds great. I have another idea. Could you not do that? Cause it takes more time when you're trying to sell them. Cause like tests kind of seem worthless to them. But if you came, again, this sounds fluffy. If you came to them and sort of said, I would like to have a meeting where we actually talk about the specific behavior of how this feature works. Cause I wanna make sure I'm building it correctly. They'd say, absolutely, let's talk. Let's have that conversation and make it very, very clear what behavior we want. So real quick side thing. When we talk about BDD behavior driven development, there are kind of two threads of that. One of them is called spec BDD. It's this whole idea that we're gonna talk about but applied to unit testing. And there is a library for that called spec PHP. And then there's another branch which is what we're going to talk about which is story BDD. And this roughly comes down to functional tests. And I think people are familiar with unit tests. Functional tests are typically described as where you actually command a browser to go to your site, click on some buttons, fill out a form and verify that you get a success message. So we're gonna be talking about that side of things. But there is this other library which is really, really neat library for unit testing. So a different take on unit testing. Instead of PHP unit, some people actually use spec. In fact, I wanna talk about a library later that hooks into a B-hat called Drupal extension. And if you look at that library, it actually uses spec when it's for all of its tests. So that's kind of a cool thing. All right, so scenario-oriented BDD. All right, so as I was saying, we, our first goal would be to try to create a single vocabulary across all of these different people that are part of the process because they might use different words than you were gonna use and you start to kind of have problems with that. By the way, what we're about to talk about is kind of like this dream thing where like everybody comes together, the project managers and the product owners and the developers come together in this like nice uniform process where they write these features and these scenarios. You'll see what I'm saying. That doesn't always, that's often not the reality. Oftentimes, you developers come to something like this. You get excited about this idea and you're gonna take it back to your company and you're gonna kind of grow the process like a seed. But even if the only people that are participating in what we're about to talk about is you guys, it already means that you at the development level have taken to this philosophy of I'm going to think and develop our features from a behavior standpoint, meaning it's just gonna get you guys into the mindset of asking the project managers how exactly they want things to work. And then from there, it might actually grow up the chain to other people or it might not, but that's fine because it's very valuable even in that little spot. All right, so this is the strategy that we're gonna follow with BDD for kind of organizing a feature. So the idea is that somebody comes to you and they're like super excited, it's a product owner and they just like blur it out a bunch of nonsense about something they wanna build and it sort of makes sense, but doesn't entirely make sense. And ultimately we need to put this to code. So what's that process looking like to actually figure out what the heck they want, plan it out and then write it? So here's what it is. First we're gonna define the business value for the features. So they come to you and they say here's all this stuff I want and you kind of first kind of break that down into okay, based on what you're telling me, that's actually three different features. You kind of want this general admin area and you kind of want this general widget over here and you want this thing over here. And so we're gonna define the business value for those features and we're gonna go through examples of all of these and then we're gonna prioritize those by which business value is the highest and then we're gonna go and describe them with readable scenarios and then we're actually gonna write the code behind this. So I'm not gonna talk more about those because we're gonna see all those examples. Now first tool that we're gonna use for this is called Gherkin. Gherkin is a language and it's not a language that was created by Behat or by PHP. It's just a kind of a standard. If it was created by anybody, it was actually created by the Ruby people I think maybe 10 years ago. So it's a structured language to describe a feature. So at a very high level it's like okay if we're going to describe a feature if we kind of always consistently use this structure and this language then we can maybe start to cut out some of the kind of weirdness and we can start to get a process that looks normal. By the way, features and scenarios are going to look to you guys like user stories and they are. So if you're already doing user stories and your issues you'll see that what you guys are writing is gonna be very similar to Gherkin and if you wanted to change yours to Gherkin it would just be like oh when I kind of word it this way I'll now word it a slightly different way but it's effectively just user stories. And it looks like this. So the first thing we're gonna do again is we're gonna take that pile of stuff they just threw at us and we're going to kind of, we decided that there's kind of three big features there so we're just gonna write down these four lines for those three features, okay? Now the first thing is, well it's so literally you'd actually open up a text file and write these four lines. So a feature you give it a little custom title like product admin area. And then followed by these three lines and I'll give you guys examples of these but this is very, very important. The in order to A is supposed to be the benefit or business value of your feature. So immediately what we're asking you to do is somebody just came at you with an idea you as a developer are now forced to say wait a second, why do they want me to develop this? What is the business value that they truly want to accomplish? Because it's not like they're coming to you with this feature just because they know you will get joy out of writing the code. You know they actually want something. What is it that they actually want? The as a B part is the person who will benefit. And I'll give you some examples but there's somebody in your organization some admin user or maybe some front end user. We're building this feature for them and that's very important. And everywhere else in this feature and scenario we're gonna be talking in the first person point of view like I do this, I see this. The I is whoever that person is. So we're gonna be seeing the feature through their eyes which you can see is important because we're building the feature for them. So if we describe it we should see it through their eyes. And the third part is just a short feature description. You know I should be able to do this and this and this. That one's the least important. A and B, the business value and who you're actually building this for those are the important guys. All right so we have these first four steps we've just done number one. And let's see a couple of quick examples with this. So let's suppose that you need to do some internationalization, some translation on your site. You need to add that to your site. So I know in Drupal that's something that's kind of built in but pretend like you need to code something up here. So it might look like this. In order to read news in French as a French user I need to be able to switch to locale. So very clearly the business values to read news in French and the person is the French user. That makes sense. That's the person who's gonna benefit from it and why do they care? They don't care just because they really like switching locale. They really like that dropdown you made. That's not important. They just need to be able to see the news in French. They didn't actually care about how you exactly implemented it. Once you have kind of like your features out and built out, the next thing you're gonna do is actually prioritize them. By the way, I actually go through this process for big features. If there's kind of like, if somebody just comes at me with one feature then I'm gonna do one, three, and four. But if I actually am building something huge and I have multiple features and I go through this entire process so this is something I actually do. This is not just kind of super fluffy theoretical things. So if we end up with three features like this, now it's gonna be very easy for us to read the business value and be like, you know what? That news admin panel is actually the first thing we should build. Maybe because we actually wanna get that in front of our admin team so they can start entering news before we actually allow French people to go see it on the front end. So that would be kind of the correct order to do that. So this is a really small thing, but isn't it nice? You actually listed the business value. So this is an idiotically simple step to do now because you can actually just read which one of these is most important for the business. Cool. So now we've selected the news admin panel feature so we're going to go into the scenarios, the actual user stories. So let's get a couple examples here. So there's our feature up there. So we're maintaining a list of news and this is being built for the site administrator. So the I is the site administrator and then here's our scenario and this is the same scenario. I'm just gonna kind of talk about each part here. So scenario always has this format. So one feature will have many scenarios, as many scenarios as you need below it to describe the different user experiences through that feature. And it's always gonna have these three parts, the given, when and then. The given is what defines the initial state of the system for the scenario. This is your opportunity to play God. Oh, and by the way, the language other than the given, when and then, the language I'm using here, I'm just making up. It's not like, why did he say like, given I am on the quote avenue, like why did he phrase it that way? At this point, there's no reason. I'm just trying to be as natural as possible, given the structure of the given, when and then. So given is your opportunity to play God. So imagine that you've already built this feature and you're gonna take the person, the site administrator, and you're going to sit them down and actually watch them use the finished feature that you've done. And so the scenario is almost you watching them do that. So before you have them sit down, you go, wait, wait, wait, one second, because I need to set some stuff up. So in this case, I actually need to, I'm gonna navigate you to the admin slash news page and then I'm going to let you sit down. Another example of a given, which I think I actually have after this, would be if you want them to see some news on that page, you might go into the admin interface and put a couple news items in the database and then sit them down so that when they go through the process, they actually see something on the news page. So given is your prep time. You can play God, you can put stuff into the database, you can do whatever you want. The when is the action that that person takes. So we sit them down and now we're watching them. And it's all stuff in their point of view, it's all stuff that they can actually do. I click on this, I fill in this box, that kind of stuff. The then is the observable things afterwards. And again, the important thing is observable, things that they can see. So it's always then I should see this or I should something else. Not things that they cannot see. So as we get further into this, you don't want to do things like then a record should be inserted into the database. Because they can't see that. That's not really value for them. This is actually kind of nice because sometimes it reminds me to actually put some success message because I'll be like writing my scenario and I'll realize that I was like, oh, I don't have anything. It just, I guess they hit save and then just go back to another page and there's like no success message. That's not very helpful. I should probably put one. And the and, and also, I don't have it here, but you can also use the word but. Those just extend the given when and thens. So this is actually given when, when, when, and then then. But when, when, when sounds weird. So we allow you to have and. All right, so example number two of this, and this is basically the same thing, but I just wanna highlight the given there are five news articles. That's what I was talking about. I might wanna make sure that there was five in the database before I actually had them sit down and go to the admin page and, and check out that page. Okay, so I'm just playing God there. Again, the language here is whatever language feels good to me. Okay, so Gherkin is exactly what you just saw. Gives us this nice consistent language for features and scenarios. So that's really nice. But what you guys are all here for, now that I made you sit through that, and that is really the hard stuff, is turning these into actual tests. But if you just stopped here, there is some value to what we just did so far. It's just, again, for me as a developer, it's gotten me over the years like super obsessed with the business value of what I'm doing, because I go through and think about that process. All right, so now we're gonna talk about B-Hats. This is actually a teacher. It's actually kinda hard to see what it's saying, but I like that teacher. All right, so B-Hat. And by the way, we haven't talked about any libraries yet. I've just talked about Gherkin, which is this like syntax thingy. B-Hat is the first library that we're gonna talk about. And what it does is it reads your scenarios. And by the way, it says maps each step. Each line in a scenario is called a step. So you'll hear me say that. Just a little bit of vocabulary. Every line in a scenario is a step. So what B-Hat does is it reads each step, and then it finds a function that's associated with that step and it calls it. And that's it. Like on a very low level, that's what it does. So it's gonna be able to kind of seemingly read your scenarios and do things magically, but it's really just reading each line in your scenario, calling a function, calling a function, calling a function. And of course, now that the world is using Composer, installing this is easy. It's just a Composer package. I think at this point, in the Drupal community, we've been all trying to shove Composer down your throats. It's so long that almost everybody has used Composer at this point, but if you haven't, we have a free screencast on that. If you wanna check it out. So download Composer, that's easy. And then it's literally just Composer require B-Hat slash B-Hat. The dash dash dev puts it into require dev part of your Composer.json. That's actually not important. You don't need dash dash dev. So if you're not familiar with that, that's not important. And I'm not gonna talk about it. And the result is this. Cool, so it's just like requiring any package. That was very dramatic. Duh. It's not helpful, but it is kinda cool, right? Yeah, did somebody lean against the thing down there? Everyone hit down? I mean, it's not that big of a deal. If then you guys are getting blasted in the face by sunlight. One time I was doing a presentation and we broke a fuse. And just like the lights went out, including the presentation. Fortunately it was not a code heavy presentation. It was like an inspiring, you know, keynoting presentation so I could just dance around back and forth for five or 10 minutes and they eventually fixed it. And I looked like a hero, so it was fine. But I'm glad it didn't go out here because we're actually gonna look at code. So we installed B-Hat. By the way, Composer puts things into your vendor directory. We know that. It's a glorified downloader. But there's one other thing that it does sometimes and you may or may not know that. If the library you're downloading contains a binary, something you can actually execute, then Composer actually symbolically links that into a vendor slash bin directory. Okay, so B-Hat is actually that way and so you actually end up with a vendor bin B-Hat file. If you're on Windows it actually puts like a bat file there so it's the same thing. It's not a sim link but you end up with a vendor bin B-Hat file. So that's what we're actually gonna run. Vendor bin B-Hat, you can run it with dash dash help and it gives us a bunch of commands. So if you're familiar with Gerastro, Drupal console, this is yet again another little built in command line thing that's gonna help you get your work done. Okay, cool. So to use B-Hat, we're going to need a feature file. That's what we were writing before in Gherkin with this whole feature and scenario thing. We're gonna need to write some feature files. And then we're gonna need a single class called feature context and that's actually where those functions are going to live that B-Hat's gonna call. Okay, and then optionally, and we won't need this at first but we'll add it later. We're gonna eventually have a B-Hat.yml file which is how you configure B-Hat. So you don't need one but eventually you might need to tweak some behavior in B-Hat and you can create this file and put some settings into it and B-Hat will automatically look in the current directory for a B-Hat.yml file. If it's there, it'll parse through your stuff. Cool, so it's actually not much stuff we need. Actually, we really just need a feature file and a feature context file but you don't have to create it by hand because we're gonna go back to our trusty vendor bin B-Hat and run it with dash dash init. You'll run that one time in your project. It's the most underwhelming thing of all time because look, it created a directory, created a directory and created a file. That's it. If you were not happy with the performance of this particular command, you would just delete the features directory and you'd be right back where you were a second ago. So nothing exciting here. In fact, nothing exciting here. This is what the feature context class looks like. Yeah, it's like, it's empty. Nothing in it yet. So not a lot has happened. We installed this library and we have one simple class. So we're gonna start very simple with actually not testing the web because this is just a great example for B-Hat itself. We're gonna pretend like we're testing the Unix LS utility. So we're gonna pretend that we're back and we're thinking about building this and we're having a conversation about how it should work. Actually, we finally researched it this morning that was actually built at MIT in 1961. And then later, it was carried by Bell Laboratories and they kept the rename, it was originally called list, and they renamed it to LS, more or less. There we go. So anyways, we're in 1961 and we're talking about how this LS thing is gonna work. So we create a feature file. This is literally like features slash ls.feature and we go ahead and we put our business value in there. And the most important thing I want you to focus on here is the user is a Unix user. So remember, everything we're going to do after this is gonna be from their point of view and very, very important because this is something that's very easy to mess up with B-Hat. It's going to be at their technical level. And the Unix user is actually quite technical. So we can talk about things like running commands. But on the web, your users are gonna be things like site administrator. And that means when you guys actually write scenarios for the web and you'll see this later in the presentation, you need to not use technical terms. Like do not say things like then they click on the like pound sign wrapper space, left call space dot btn button. Cause that doesn't make any sense. If you start kind of going down that road and making it too technical for your users, then you start to look at these features and scenarios and you realize they're not really human readable descriptions of the feature anymore. They're just programary descriptions. So ideally you want to be able to take your scenarios at the end of the day. And even if you don't, you should be able to take them and actually give them to the person that you're writing the feature for. And they'd be able to read it and be like, yeah, that's totally what I want. That makes sense. It's almost like documentation of how the features should work. So we're having this conversation and we say, you know what? Let's think about how LS is gonna work. If you have two files in the directory and you run the command, then you should see those two files. Great, that sounds like a scenario. So let's write it. And again, I'm just making up this language here. I'm saying, given I have a file named foo and I have a file named bar and when I run LS, that's so me playing God and then me doing my action, then I should see foo in the output and I should see bar in the output. Cool, so that's all we've done so far. Now we're gonna run behat, vendor bin behat. Behat is actually gonna parse those and magically do it. No, I'm kidding, not magically do it. There's no magic going on. So this is not gonna work yet. In fact, you see it says like five steps undefined. So it's kind of looking at all of those steps and trying to find a function inside of our feature context. There's nothing inside of our feature context yet. So it's like, dude, you need to put functions inside of your feature context for all of these. So here's what I'm gonna do. I'm actually gonna write them for you. So below the command, it actually dumps out. It recognizes you have these languages that don't have a function yet. So it dumps them out to the command line and basically says, hey, you should copy these into your feature context class. And this is actually a slightly better view of what they look like. These use annotations to form a very simple pattern to match your original step. So you can see above the first one, I know that gray is a little bit dark, but it says at given, I have a file named colon file. That colon file syntax or colon whatever syntax is basically a wild card. And it did that because it recognized that when we ran our, when we wrote our original commands, we surrounded those by quotes. So anytime you surround something by quotes, it's like, oh, you probably want that to be like a wild card. So now you have a kind of a reusable language. So it recognized that and it created a something generic that can be reusable. Okay, so this is just me literally just copying that stuff you saw into my feature context. I haven't actually touched it or changed it yet. By the way, you can also use regular expressions up there if you need to do something like really kind of crazy. You can do regular expressions and say like, this step should match like this word or this word or this word, but not that word for you regular expression ninjas out there. All right, so we copied the definitions and now we just make those definitions do what they're saying they should do. So literally we have one called, I have a file named, how do you create files in PHP? Easy as way, just touch, touch, let's just touch those. Given I have a directory name, okay, makedir, let's make that directory. When I run, okay, cool, when I run, we're gonna run a command. We all know there's like 55 different functions in PHP to run shell commands. So you can pick your favorite one and call it up there. Now the one tricky part about this, and this is very, very important, is sometimes you kind of need to share information between your functions. And this is a perfect example. When I do I run, it's gonna get the output and our next function I should see in the output, it's gonna need to be able to get access to that output from that command so we can actually look inside of it. When you have this situation, you're actually going to create a property. I'm not showing it here, but you're actually gonna create a property in your future context. In this case, I have an output property. Just kind of imagine it's up there. And you're just gonna take advantage of that property and set the output on it. And then later down here, you can just reference that property. Good, yeah, output is not a reserved keyword, I just added it. And that's what I want you guys to feel very comfortable doing, adding those things. The most common one in the web land that you're gonna have is the currently logged in user. Cause you're gonna start so many of your scenarios with like, given I am logged in as whatever. And then later you're gonna be like, and I create some blog posts that I authored in the database. So then you're gonna have some code that needs to create those blog posts. You're like, blog posts authored by me? Who's me? Well, when you log in, you're actually gonna set the user object as a property. So every other line after that, you can go and fetch and actually see who you logged in as, get that user object or maybe get the user ID or something like that. All right, so we filled them in, we run vhat again, and that's it. It actually runs through and is calling each of those functions. Those functions are physically creating files. We are physically running that command in the background and physically looking at the output. And now our steps pass. So if you wanna see the full version of this feature context class, it's up on this little bitly here, bitly slash vhat dash ls.feature. There's a couple of other things that add like cleaning up the old files. We have like random files and directories floating around for the next test. But in essence, that's all that you need there. What a great question. You're already thinking ahead. I actually don't mention that, but I'm really glad you asked that. So is the feature context class object created once per the feature or the scenario? Very important. Your scenarios should be independent of each other. So when you start having multiple scenarios, I want you to look at every scenario as its own unit. So don't create a user on one scenario and then expect that user to be there on the next scenario. And the practical reason for this is when you start debugging scenarios, if this scenario is failing, you don't wanna have to run like everything to get down to that. You wanna be able to target and say, bhat, just run this one scenario here and so I can see if it fails. So to reinforce that, you get a fresh feature context object per scenario. Yep, so you have isolation between your scenarios. Very good question. So what bhat does is we write a scenario step like given I have a file named quote foo, it generates that pattern for us, creates that method and the pattern. Oh by the way, so we have steps, which are the lines inside of our scenarios and then definitions are the functions inside of our feature context. So here those called step definitions. So that's the last bit of kind of terminology there. And then of course in those functions, we do the work, like touch the file and then failure is defined, failure or non-failure is defined as like every single step that's called, every single function that's called, if it doesn't throw an error, it's a pass. If you throw an exception from inside of there, then it's a failure. So very simple. All right, so yeah, so creating files and directories is neat, but you guys are here for the real stuff, for commanding a web browser and doing cool things with that. So we're gonna talk about Mink. Mink is actually the cool guy behind this library. So Mink is a standalone library by the way, so you could actually just use Mink directly in your project and Mink is actually used to do some of the core testing inside of Drupal and I'll mention a little bit about that later. So Mink is just a PHP library and allows you to command a browser via PHP. So literally you write PHP code that's like, hey, you should go to that page and then find this link and click this link and submit this button and all that kind of stuff. And by browser, it could be anything. You can use Selenium and I'm gonna show that later to actually open up Chrome or open up Firefox. Or you could use Curl in the background. So don't open anything up, just do Curl in the background. That Goot driver, that's actually the Curl one that does Curl in the background. Or PhantomJS or ZombieJS or any, well, not anything else, but in theory anything else, but that's all there is. So let's get a sample, just a little bit sample of the Mink code. Mink is probably my favorite library in PHP, but it's also quite small. There's not a lot to it, so there's not a lot to know, which is very good. So in Mink you have four important objects and that's it. You're already seeing two of them. So when you start, if you were gonna write Mink straight up in a PHP file, you'd create a driver and the Goot driver, what that's saying is when I actually open a browser, don't really open a browser, just make like invisible background requests through a Curl. But if you change that to Selenium2 driver, it would actually open up a real browser. The driver is actually the least important object out of those four, but it's there and that's what it does. The first real important object is the session. Think of the session like a browser tab. Anything you can do with the browser tab, which is actually not much, you can do with the session. What do you do with the browser tab? Well, you enter a URL and you go to it. Maybe you refresh, maybe you go back, maybe you go forward. That's basically what you do with a browser tab. That's what the session does. And you do it, that's the most important method right there. Session arrow visit, you give it a URL and it actually visits that behind the scenes and you can get some basic things like the status code and the current URL off of that. All right, so once you have the session, that's kinda cool, but not that cool. We're gonna go and get the page. Important object number three, the page. Think of the page as the jQuery element. Yes. Good, yeah. Does it wait for the page to load before you start, because we're about to start doing some stuff on the page? Yes, it does. And that's actually up to the individual driver, but the drivers themselves, like Selenium 2, knows to wait to go and wait for the page to load before it goes on. Goot does the same thing. Obviously, Goot's just curl, so it makes the full curl request, so yes. However, and I don't show it in this presentation, that starts to break down a little bit and is one of the tricky things, but I'll give you some resources at the end to cover it when you do things with JavaScript. If there's a full page reload, you're good. But if you click, and a half second later, a modal comes up, because you click and you made an Ajax request, and then the modal came up, and then there's a button you want to click, then you're actually gonna need to start kind of adding little weights in your code. That's a very, very important thing to get right in Behad. If you get that right, you're gonna be very, very happy. But for the most part, full page reload's no problem. So important object number three is the page. It's basically the jQuery element. Anything you can do with jQuery, you can do with the page, which is just finding elements and then interacting with them. The most important method is find. So we're gonna find with CSS, li, colon, nth, child, 4a, whatever that was. I think it's like one of the nav items on Behad's website. And then, again, just like the jQuery element, actually, let me say here. So the page is the third important object. The last important object is what you get back once you've selected something inside of the page. And actually, just like jQuery, you can select an element, and then you get a result back, and you can actually find inside of that. The kind of jQuery object, or whatever you wanna call it, the global thing, is actually more or less the same as once you've found something, you can do the same actions on it. It's the same thing here. The page and individual nodes, individual elements that you found, like our dollar sign E-L-E there, they actually have all the same methods. E-L-E actually has additional methods on it for actually interacting with elements, like it has a method called click. Because you might wanna actually find a link and then click it. So anyway, so that's all the four important objects there. So we find something with CSS, and now we can say get tax, get attributes, click, and that's actually going to take us to the next page. And actually, there's everything your heart could desire. There's double click, right click, mouse over, mouse off, drag to. I haven't found anything yet that I haven't been able to manipulate on an element. It's all supported, even stuff that is very JavaScript-y, it's supported. Now, of course, some of you guys might be thinking, if you're using the Goot driver that uses curl, then JavaScript is not being executed. But if you use selenium, or phantom, or zombie, then JavaScript is being executed. So that will work just fine. So knowing what mink can do, if we could get access to mink inside of our feature context, we would be very, very dangerous. Because now we could start writing steps like when I click on this button, and we could actually write functions that use mink to go find that button and click it. So to get bhat and mink to go together, we actually could do it by hand. But there's a library, it's basically a plugin called mink extension. Extensions are the plugin word inside of bhat. So you install this, it's just its own library, I'll show you in a second, and it's just gonna kind of marry these two together very easily. So we need to get mink extension, so we are going to update our composer.json file to get, oh, I actually don't even need all that, I'm gonna go right here and show you this. We're just gonna grab a couple of new libraries. We're gonna actually grab the mink extension, and then based on whatever driver you want, you need to get those individual driver libraries. This will give us access to the Goot driver to make curl requests or the Slunium 2 driver if we want to open an actual browser. And the result is, looks like that, but that's not that important. All right, so our goal is to get access to mink inside a feature context, and finally, finally, finally, we need a bhat.yml file, because that's what tells bhat about the extensions. Hey, we just downloaded this mink extension, so can you kind of go talk to it, let it do its magic? And there's not a lot that you need to configure in here. All right, so there's two steps now to actually get this working. So what does this mink extension actually give us? Well, thing number one is, you're gonna make your feature context suddenly extend a raw mink context. And what that's gonna do is it's going to give you access to a get session method as in the mink session, important object number two that we just talked about. It will create the mink session for you and just hand it back to you. If that's all it gave us, we're dangerous, because we can visit pages. We can call session get page and get the page and start finding elements and clicking those elements and all that kind of stuff. So it's totally unlocked us here by giving us access to that session object. But wait, there's more. We're gonna do one more thing. We're gonna go back to that bhat.yml file, and we're gonna add a little bit of configuration down here. So when bhat loads, it needs to know all of the definitions, all the language that we have in our app. So right now it just looks at our feature context file and just reads those annotations. But you can tell bhat to look at other classes too. This gets really interesting because you can start thinking about there being third party libraries that actually give you more free step definition language. And this library gives you a class that looks like feature context with a bunch of annotations in it. And it's called mink context. And via this configuration down here, you basically say also go look at that guy and parse its annotations. So originally, so far we've only had three bits of language. So by the way, if you run bender bin bhat-dl definition list, it will give you your full menu, all of your tools, all of the language that you can use. So we had three things before, the three things that we did. After we do that line, it looks something more like that. Can you guys read that? So it's everything as far as like I go to this page, I click this link, I look in this element. It's like all the most fundamental basic stuff for integrating with things on the web. Which means when you install this, you can actually start writing test free application without writing any actual functions, any of those step definitions. Because you just have a bunch at your disposal. So let's look at an example. Let's suppose we're actually testing Wikipedia. So we're not testing our local copy, we're testing some public website. And the details of this are not that important, except for the first time, I keep telling you that the language you use in your scenario doesn't matter. That's true, but we have a bunch of built in language now. And if we can use that language, we don't have to do any work. So all of these lines here, the given when and then, those are all coming from that list. Those are free built in functionality. So we're gonna just write this scenario and it just passes. Now right now it's actually, if you envision this, it's actually running, you don't see anything pop up yet. It's just running curl requests in the background, but it is making real web requests out to Wikipedia. All right, so let's get to the last part here, which is actually BeHat in your applications, specifically inside of Drupal. So there's a, what I just showed you is kind of considered black box testing. It's where you're actually testing a public website that doesn't live on your local machine. And that is a way to do it. There's QA departments that use BeHat to test their staging server. It's not something that's on their local machine. The downside of that is, I mean the upside of that is you can just spin up a feature file right now and start testing Wikipedia. Boom, done, love it. The downside is you don't have access to prepare anything in the database or have access to your code. Because cardinal rule of testing is that you should start every test with a predictable data set. You should know what users are in the database. You should know what products are there. Because otherwise it gets weird, right? How do you write a test for your staging website and you log in as a user, but somebody deleted that user on staging, so now all of your tests fail? You're just assuming this user is supposed to be there but somebody changed their password? So we don't want that on our local machine. We actually wanna say, before this test, go into my Drupal install, make sure this user is there with this password. So in my scenario, I can actually go log in as that user and it will always work. So we wanna add nodes, users, permissions. We wanna be able to do all this stuff from inside of Behat, inside of our feature context class. So how do we do that? So we're gonna install Behat, so just think of like fresh project, fresh inside of our Drupal project. By the way, Drupal, Behat has nothing to do with Drupal 8, so you can do all this stuff inside of your Drupal 6 project. This is just a standalone library. Okay, so we're gonna install Behat and make into our project. We're going to gain access to the Drupal functionality from inside of feature context somehow. And you guys know where this is going. We're gonna profit. I mean, we're going to create nodes, users, et cetera so that you can test against a predictable dataset. So fortunately, there's a library made by the Drupal community which I did not help with called Drupal extension, which makes this stuff a lot easier. That's the handle for like the main author behind it. So if you see him like high five him, I actually don't know if he's here. I haven't seen him yet. Gonna see if he was like in the room. No, he's either not here or he's trying to be shy and not being here. Anyways, this is wonderful. I'm gonna show you what it does. So it gives you a number of things. It gives you even more built-in sentences. So just like the main extension gave us that list. This gives you even more stuff that's specific to Drupal. So usually logging in with Drupal is kind of the same flow. So there's some built-in stuff where like I log in as this user and it will go behind the scenes and actually create that user and then go through the flow of actually logging you in as that user. So really, really great stuff that you would have needed to build yourself. It helps you build nodes, add users, manage permissions. It gives you lots of functionality from within the feature context class to do that kind of stuff. It also has this idea called regions, which I think is really neat, kind of allows you to configure different sections of your site so that when you click things or assert that things exist, you can kind of say, when I click edits in the sidebar, in the sidebar, and you've actually defined like where the sidebar is on your page. So it's kind of a cool idea that they added to that. And it's also as hooks to load more sentences and definitions from contrib modules. Not sure if that's something that's being done yet, theory contrib modules could actually like add more sentences to your application because maybe they added some new functionality and they want it to make it easy for you to interact with their functionality. So this is a real example here of what it would look like. So let's walk through this. So given, because we're gonna play God, so before we start this, we're gonna need to have a user and we're even gonna log in as a user beforehand. By the way, if you're testing the login system, then logging in would be in your scenario. But if you just like, it's just a stupid implementation detail, you're really worried about doing something once you're logged in, then you can put it up and you're given. I didn't talk about background, but background is a little thing you can put the top of your feature file so that it runs before every scenario. So we're gonna do a little given up there. And then given, I am viewing a page with the title cool beans. That's actually gonna create a page node type with that title and then navigate you to it. Built in definition for that. And then down here, this is the region thing I was talking about. Then I should see ipsum in the body region. Body is something that I would have said is something within this CSS selector. So it looks inside of there automatically. And that's it. This actually runs and it actually passes. So you can kind of imagine the work of setting that stuff up. So this goes a long way to setting that stuff up for you. And of course, we know even more importantly, it's a huge example of how you do stuff. So when you get that edge case where you don't have something built in quite that this covers, you can open up a source code and see that, well, how is it creating users? Cause I need to do something slightly different. So the three modes of the Drupal extension are black box. There's like three things you can actually configure how it interacts with your Drupal install. Like how is it creating users and nodes? You can use black box or basically doesn't. And this is for testing your staging site. Or you can have it go through Drupal itself actually like requiring the PHP files. Or if you want to, you can actually have it use drush behind the scenes to go and create users. And some features are not supported by one and not supported by the other. So there are pros and cons to each of those. It just depends on your situation. So this is what I've been coming up here. What if my page relies on JavaScript? Cause so far we've been making fake requests. We've been making curl requests. And the answer unfortunately is that BeHat does not support testing pages that use JavaScript. Just kidding! Ah! No, you just add that above, add JavaScript. That's all. That's all you do. It's all good. It's fine. As soon as you do that, it's going to, assuming we're using Selenium. Cause when you mark that, you can actually hook it up to use Selenium or PhantomJS or a ZombieJS. But let's assume that we've configured our BeHat.YML to use Selenium. We've installed the Selenium driver. As soon as you do that, you rerun the scenario. It's gonna pop open a browser. Well, there's one last little step here, which is you actually need to download Selenium. That is not scary by the way. Selenium is a jar file. It's like a PHP FAR file. It's a single standalone file. If you have Java installed, then you just download the jar file and you run javaspace-jar and then the name of the file name. And it'll just hang there because it's actually started a server. It'll just hang there and wait. And now you can actually use Selenium. So this is literally the only kind of the hidden secret dependency that's gonna ruin your life with all this. That's it. It's those two lines and it works all the time. So obviously don't need to memorize that URL. Just Google for download Selenium server, but it's something that's gonna look like that Selenium server standalone thing. And it's really simple. So assuming you have that running, yeah, that's just gonna boom, pop open Firefox or Chrome. You can configure it to use Chrome or I think you can even configure it to use Internet Explorer or something like that if you really wanted to. So bonus is, because this is kind of a new thing, part of this is new in 8.1. If you, so Beehive is really, really wonderful. If you just wanted to use Mink inside of your PHP unit tests. So you actually want to, inside of a unit test, not really unit test at this point, use Mink to just manually just go to this page and click this link just using the Mink actual PHP code. You can do that very easily. In fact, there's a browser test base is something that's built into Drupal's core to help you do that. And there's even a subclass, a new subclass called JavaScript test base which helps you specifically do that with Phantom.js. So like I said earlier, Mink is already in the core and it's being used, I don't know exactly how extensive it'll be in the core but you can take advantage of what's in the core already. All right, so the Apple log, last thing here. It's simple guys. You're going to install Beehive and also Mink. And by the way, we have a big old screencast on all the stuff on our site, so please go check it out. Sneakpeak, I'm gonna give you guys a coupon code in a second so you can actually go and get that for free. It's brand new, so it's really, really good. I'm biased, whatever, but it's good. And step two, write features for your app. And you can start very simple. Don't feel like you have to save the world. You can start writing scenarios and features that are like, go to this page. Make sure it's not a 500 error. That's gonna do a lot of damage out of the box and then you can ease into getting a little bit more hardcore after that. So don't try to save the world in the first go. And that's it, high five your teammates and enjoy. So thank you guys very, very much. You guys have any questions? You can raise your hands right now, yeah. Oh, for the slides for this? I actually don't have the slides for this up yet. Oh, I think I had URLs of different code things. I'll attach them on Drupal.org. But they're gonna have this up on video in like two hours anyways, but I'll attach it to the presentation itself. Yep. Suggestions for CI services. I used Travis CI. Now I use Circle CI, which seems to be a little bit easier to use. So obviously you can also use Jenkins, but you have to do more setup. But Circle CI works really well. One of the things that's very easy to do with Behat that it didn't show is taking screenshots on failures. And with Circle CI, they have kind of just a directory you can put those in. And after your test runs, it'll just show you in the web interface. Like here are the three screenshots that came out of your application for the three failures. You can see how it gets hard, right, when you're like it fails, but it doesn't fail locally and you're like, what is going on? Yeah, Circle CI is my favorite. Yep. Yeah, so how do I, strategies for getting that consistent starting data set. So there's kind of two strategies. One, if you have a very simple application, like a symphony application a lot of times, you're in charge of the whole thing. Your database structure is not that, you don't need data to start. You can actually empty out your entire database every single time. So in a lot of my symphony apps, I actually do that. I basically truncate my tables and then I go and add the things I need in my background. However, in other applications, you have things like lookup tables. We're like, look, there's a certain data that needs to be there on Drupals that way. Or like the thing, I can't truncate my tables every single time. So a more kind of real world version of that, that I've seen people do is basically get their system to a blank state, but you know, not a blank state, but kind of a good starting state where you have things kind of configured the way you want them to. And then use SQLite. So you basically get an SQLite file that's stored at that step. And then you basically commit that to your repository. And then at the beginning of every test, you move that to the correct location or copy that to the correct location and then you're good to go. At the end of the test, you just delete that. Next test starts, you copy that over. So try to minimize that though because what you don't want is your scenarios to start looking like this. When I log in as this dude and click this product, you're like, who is that dude and where did that product come from? You know, just try to keep it as minimum as possible. And if it's things that are actually very relevant to your scenario, then put them in the background so you can actually see them. It reads like a story. Yeah, sorry, I had three of you guys in one section. Yeah, go ahead. Can you have two scenarios depend on each other if you wanted them to, in theory actually yes because other than you getting a new feature context object, there's nothing that happens between the scenarios unless you do something. So that whole SQLite thing I was talking about, it's your responsibility at the beginning or at the end of a scenario to do that. So technically, if you don't clear the database between scenarios, then yeah, your database would be shared across those scenarios. Yes, you can do both. So you can use, I'll repeat his question in my answer. You can, there's a hook that runs before all the scenarios. There's an at before feature hook. There's an at before scenario hook. So you can hook just before the scenario. And there's also a before sweet hook, something that literally runs one time even if you're about to test 15 different features. And the hooks are very, very simple. I didn't talk about them because you put a method inside of your feature context class. You put an annotation above it called at before scenario. Boom, that's being called before every scenario. So you can hook in a lot of different levels. Yeah, we're here in orange. Yeah, it's typically, so where does this go? Development, staging, production. This is typically done on your local development machine and also on your CI machine. Like how I said, however, some people like actually test against production. But then you're like I said, your black box testing and you're just, you have to be like kind of very hands off. You don't really create data in that case. You have to kind of make some assumptions about data being there. Yeah, usually it's done locally. So we do set up our local environments to be able to run B hats. However, because we have CI set up, we typically don't, I never actually run the entire test suite locally. I just run the scenario or a couple of features that are relevant to what I'm doing. Push it up to CI and have it scream at me that I messed something up. Yep, and one more over here and I'll move over. Oh yeah, there is window size settings, yes. So that's what it basically comes down to. It's gonna be using a browser. You can select the browser that you want it to use and you can also, if you want to, select the size of that. So if you want it to be like, oh, I'm gonna shrink it down really small. So that's basically what it comes down to. You might also be able to like fake the headers or something if you're looking into that. Yep. No, this is not a continuation of that. Perfect. Oh, that, okay, cool. Giant feature context classes. Yeah, so just somebody that's actually worked. So there's a one o'clock Birds of the Feather, apparently, on this topic. So yeah, one of the things that can happen is you can, you're focused so much on the language, you can end up having gigantic feature context classes, just function after function after function, especially because it makes it so easy to create those. So you can break your, this is a non-answer at first. You can break your feature context into multiple sub-contexts and that's something, it doesn't give you less lines of code, it just helps you organize. Like this is my admin context, this is my product context, and they can communicate with each other. And then in my experience, like the best thing for reusing code is, it's not a great answer, but it is the answer as far as I can, in my experience is like just having private functions inside of your feature context. Private functions, like I, you know, I have these three different definition functions that all in the middle of them do the same thing. They all create a user or they all do some sort of flow. So I'll have each of them just call that private function, extract that out to a private function and do the work inside of that private function. That goes a long way. Yeah, that goes a long way. It's still up to you to kind of keep things organized. And yep, back there. I was gonna put one more mistake. Have you, are you talking about with the Drupal extension specifically? That's actually, we should find Jonathan and ask about that. So the question about like with Drupal extension, it comes with some set up, some teardown stuff to kind of like clear out data and clear out certain data. So it clears out some data, it doesn't clear out other data. So without knowing the specifics of it, the answer is if you want to delete something specifically, then you're going to need to do it in your, basically before scenario to actually go out through the Drupal API and actually delete those things. Or do the SQLite dance that we were talking about where you kind of save that state. Yep, so. Yeah, yeah, so let's think. Is it worth you to use the local copy? No, it doesn't, yeah, it doesn't force you to use the local copy. It has to do, it's a configuration, I think it's a configuration in behad.yml. So you can configure, you actually configure, you can configure the actual URL to go out to, to like talk to Selenium. So you can actually just change that to go out to a grid URL, I would think, and you'd be just fine. Yeah, technically you do at JavaScript also, but the other way you can do it is at mink colon selenium two or at mink colon phantom js. You can actually select the specific drivers if you want to. Yep, over here. Yeah, is it easy to find information on these hooks? It is, if you Google for behad hooks, you'll find it, it's very simple. Also, our tutorial on it talks about hooks and actually shows you in a real application, it's not actually a Drupal application, but in a real application, you'll see me using the four hooks to clear out data and do kind of some real world things with it. Yep, back there. Yeah, yeah, so the question is, hey, hey, I've done behad before and sometimes at the end of the test, I will just literally make a query to the database and say is that product there? So that's the thing I told you not to do, right? Don't say something like then this should be in the database because the user can't see that's in the database, but sometimes reality comes together and you're like, look, this is actually, sometimes it's just like, oh, look, if I click this button, I see the message, this is like using some core functionality, maybe even some Drupal functionality, I know it works. It just, it always works if you see that message and that's what I want you to do first and foremost. However, if it keeps you up at night, then do it. Then I should see this in the database and make the query and feel okay about it. Yeah, so the question is like I have users that have like very complex roles. Do I need to, should I just have that in the database beforehand or should I like actually set that up in my given beforehand? Obviously it's up to you. If you have a certain set of, well, it's up to you. First of all, say that. But if you do put it in your given, that would be like the preferred way of doing it. If you do put it in your given, you can shorten it to kind of say something like, given I have a user and just kind of use some descriptor, given I have a semi-admin user, something you guys actually use to describe those people in your organization and then offload all the ugliness to your feature context file so you can just repeatedly say, given I am logged in as that type of user, then over here, given I'm logged in as that type of user and it's all just going to that same spot so you don't end up with scenarios that have the same 10 complex things over and over and over again. No, exactly, the role would not need to start, not need to exist when you started the test, you would actually put that in your given. You don't have to do it that way, but that's the ideal way because it makes your test more meaningful. Yep, yeah, so how do you handle credentials? So this goes back to if you're testing locally, it doesn't matter because you're testing locally and so basically you're having them log in as like admin, admin. If you're testing black box testing, then it's kind of up to you. It's kind of a classic thing of like, how do I not hard code? My database password into my application. So these things like, there is a spot, I can't remember exactly how you do it, but there's a spot in BHAD.YML where you can kind of put parameters. I think there's also something where BHAD.YML will automatically read certain environment parameters and kind of pass those to your future context class. If you Google BHAD environment parameters, it's in there, I just can't remember exactly how you do it. So that would be a way you could, if you want to be very secure that you could avoid hard coding those into the middle of your future context class. Take that in their application. So they say look, like the whole SSO thing, happens to be in every single scenario. We don't really need it. We just need the application to think that we're logged in. We're good with that. Maybe we even have one or two scenarios that test the SSO if you wanted to. So at that point, there's no magic bolt for that, but you'll do something that effectively tricks your application into going into it. Like for example, I mean just, you'll have locally, you'll turn on some environment parameter or something. So when it's making web requests to your application locally, you have this little flag set to true and you have a little, you know, basically a security hole to kind of auto fake log you in as that user. It's a very pragmatic thing to do. Any other questions over here? Yes, but not, I'm not, so is there a way to orchestrate visual regression testing? Actually, somebody asked me at Drupal about that last year and I thought they were like gonna build something. I can't remember anything about it. I'm not aware of anything that's out there to do that specifically. But yeah, I mean, in theory, right, you could do that, yep. So yeah, so real world thing, she has multiple modules and there's a feature context in each module. Some of them are big, some of them are small, so should we break them down further? Obviously it's up to you. But my, I would have started the way you started the very least because on day one, create like 15 feature context files all by different themes and start feeding them together, you know. And then kind of torture your team as you're learning B-Hack because they have to figure out which file all of these things are in. So I would start very simple with like a single feature context file. But are you guys suffering from duplication between those? Yeah, yeah, yeah. So it sounds like, as soon as you have duplication, yeah, it sounds like you guys should do that. And you can just do it little by little. So you might create like a user context. I do this in my applications sometimes and it's even in ways that aren't even very nicely organized or impressive. I have created, I've had multiple feature contexts like that before and then I've created an abstract feature context which is like when they all, they all use this one. I just jam all the reusable stuff into one thing. So you can do that. Or if it becomes obvious to have different categories, do that. But yeah, you should, if you have duplication between those, then yeah, definitely run for it and feel very good about making, they're called subcontext. They look just like your feature context files but subcontext is sort of organized things by theme. And yeah, that's a good way to do it. Yep. Yeah, so the more these things you write, the longer it's gonna take to run your tests. And it's especially true because these are functional tests. So you're actually like spinning up a browser and a very simple functional test might easily take five, 10, 15 seconds. So like yeah, your test suites can start running really long. So a couple of things I'll say on that. One is like the best thing by far is having CI because it doesn't matter how fast you get your tests. If you're doing a good job writing these tests, you're not gonna run all of your tests. So get hooked up with CI so that for the most part you don't have to worry about it. Second thing is you're gonna lose most of your performance with the data loading in the beginning. So that whole SQLite thing I talked about, that's what people end up typically doing at least in my world in the symphony world but what drives them to that is actually not that they need to have some data set up but that they actually want a very, very fast way to reset their situation because maybe what they were doing is actually clearing the database. And then at the beginning, they were kind of loading fixtures. They were saying, okay, we'll clear the database. Now in PHP we'll like insert five products and five users and all that kind of stuff which I said don't necessarily do that but some people do that and their tests started to slow down so they moved to this SQLite thing. The third thing is if you have especially like an SSO authentication if you can basically short circuit that then you might save yourself five seconds per scenario times a hundred scenarios equals like five minutes off of your testing time. Yep, and then like make them faster. So you're talking about, you've read a blog post about like going to the mink extension class that gave us all those free extensions to make them faster. I've never done that. They're usually all pretty straightforward. They're like click this link or fill out this field. Oh the Drupal extension, yeah. So that might be true. There might be certain things that Drupal extension is doing that. Maybe you're like, oh, when I call this thing it's actually doing these four things now I need to do these two things. So yeah, that's totally possible. And again, probably that stuff is probably a lot of like data loading. You're like when I called this I didn't realize it was also creating a user for me. I don't need a user. So maybe I'll kind of copy and paste part of it out and reuse it. And I'll also say if you have this problem again another thing you can do is parallelization. That's something you'd use on CI but you can run your tests with parallelization. It's just doing web requests. So in theory if you have a server you could basically divide your tests into two and run two processes and now you have two browsers going in the background or even across two servers. It takes a little more work but there's nothing wrong with doing that. As long as you have two databases, yep. Yeah, so test coverage with this stuff. I don't know the answer to that but I know and I can't remember exactly. There are some people that I've actually looked into running test coverage with BeHat so just Google for it. I know that's a bad answer but I don't know that much about it because I'm one of those people I don't worry about test coverage that much. I'm like on the really pragmatic side of testing so not that worrying about that stuff is bad but I don't worry about it that much. But I mean the real answer is you're not supposed to because functional testing it doesn't like directly test your code but I think there are some people that have done some stuff with that. Yep. My general rule in testing is I unit test when the function scares the crap out of me and I functional test in a lot more cases especially when those cases scare the crap out of me. So I don't have as many unit tests as a lot of other people. Any other questions? You guys are awesome. Yep. Oh yes. Social authentication. Yeah so in theory you could actually do social authentication, real social authentication with BeHat and Mink. With Mink you can actually switch windows so if you open a window if you know the idea of that window you can actually have Mink move to that window and start interacting with it. It's a bit of a slippery slope because you need to have a real Facebook account be logging with real Facebook credentials to come back but yeah in theory you could do it and it would be the window thing. What we did in our application because what we really wanted to know so here's what we did. Our application is a little bit complex because once it comes back from Facebook we see if they already have a user in the account and if they do we do one thing, if they don't we do another thing. You know there's just kind of three different cases they go, we log them in or sometimes we have them like add their password or sometimes we like send them through a finished registration so there's some complexity on our side after it comes back. So what we did is we faked it and we basically said the test goes when I authenticate with GitHub if that's actually what we're authenticating with and what that actually does is it goes to a specific URL. It's actually the second, the redirect URL but it sends like a special fake token up there and we basically hack the whole into our security only in local environment that says like if the token is equal to this then just kind of fake that this date is coming back from the API. So that solves the problem for us of being able to very quickly test those different scenarios what if it exists in the database but if it doesn't exist in the database they get sent to the right spot and then for like does it work? Like can I actually go and authenticate with Facebook? We just use actual like old school like let me try this out on my browser and make sure I actually have the flow correctly. So anything else? Anything else? All right, well I'll let you guys go to lunch. Thank you very much. You have more questions to come up after this. Thank you, thank you, thank you.