 Hello, everyone. My name is Eduardo Gutierrez. I'm going to be talking to you today about ambitious capybara. This talk is brought to you by some very special people. I've been unemployed for most of the time when I was working on this talk for the past few months. So I don't have any actual companies that I'm sponsored by. Instead, I'm brought to you by these restaurants in the Boston area that will let me sit in their restaurant for like six hours a day coding because it was getting a little bit old sitting at home kind of like even with all the snow I had to get out at some point. Additionally, I'd like to give a huge thanks and shout out to the Boston R.B. Ruby group. I gave them a sort of early version of this talk. They gave me a lot of really great feedback. And they've also just been a really great community for encouraging people to talk, encouraging people to like get their voices out there. So I wouldn't be here today presenting if it wasn't for them. So now I have a very deep confession that I want to tell you all. It's it feels awkward just based on like sort of a lot of the projects I've worked on in various Ruby and Rails projects, but I'm in love with capybara. And maybe this doesn't seem weird to you, but the number of times I've seen developers go into like a feature file and kind of just like slump over just like just like what's going on here? What is this? Like this is test code, not even like old production code. And just the number of times I've encountered developers who just have very little faith in sort of integration tests due to like, you know, some bad experience or some complexity that they experienced in the past. I've worked on a lot of projects where like integration test quality sort of slid in favor of sort of more unit tests or just sort of dealing with the burden of tests that would fail every now and then or test the, you know, you don't have a lot of confidence. And so you have this very big app and you just sort of always like pins and needles as this feature going to break something else in the application. And so my goal today is to sort of dispel that for anyone here in the audience or to give you the sort of tips and tricks to take back to your co-workers, your friends, to sort of reestablish that confidence because when capybara first came out it sort of changed the game and what the kind of ambitious applications we could build with Ruby on Rails and in general any sort of Ruby based framework or not at this, at this point. And so the ability to test JavaScript to make assertions without having to couple or test the specific parts of the DOM and how it's laid out I think has gotten, has been a significant, significant contributor to where we are today and sort of the kind of applications we see out there that are built by Rails and sort of inspires to build and create bigger and better things. So I'd like to start out with some little known facts about capybaras. Capybara or the hydrochorus, hydrocharis originate from South America. And doing research for this talk I discovered the pronunciation of the name is under heavy debate. I've heard some people pronounce it as capybara versus capybara. It's still up for debate. I've lost some friends over this. Some very advanced facts about capybara is they're semi-aquatic. And I think that makes sense because capybara is like kind of like the giant biggest web rat, so it has to traverse the internet tubes underwater. They can roam over areas spanning 25 acres. To me that means that they're capable of handling very large applications. And they're very friendly animals. A lot of people have them as pets. So it's a very approachable framework or not framework tool. So if you've had a bad experience I hope to encourage you to check it out again. So I like to talk about balancing that I encounter a lot between readability, maintainability, and performance. And I find that this is sort of the core of what it is to like build a software project to balance sort of how readable the project is, you know, how easy can anyone else get into it and understand what's going on, how maintainable is it, you know, can someone come in and easily make changes without compromising the rest of the application, and then does it all perform very well? Some might say sort of, I've heard from you about that readability and maintainability are kind of the same thing, but examples like counter examples I have to that are you can have a very readable DSL like the Rails router. It makes a lot of sense, but if you go and look under the hood of like the complexity required for maintaining that nice DSL sort of it's, you know, there's a lot of overhead to that. And then on the other side of things you can have very maintainable well-structured well-composed code, but it's still very difficult to like get into even though it's well-structured there's still a high learning curve. And so then our ideal is just, you know, be right in the center. And when it comes to integration tests I tend to aim for just below the center as my sweet spot for like, I'll worry about performance later I'm more interested in sort of my test serving as documentation that either a stakeholder could someday use or a junior developer or, you know, a senior developer hasn't seen the code in a while. So how would I describe these things for Kappibara? So readability means your future test should be written as user stories like sort of throwing back to the days of Cucumber feature files you want it to be something a stakeholder could read you want it to be sort of clear as many technical details as possible even though it's code you want it to be, you know, live in that Ruby essence of like very readable code. For maintainability you want feature tests that are going to be able to grow and easily be refactored as your features and your product grows. And for performance you want fast feedback either on your CI server you want to know if any of your changes broke some other ancillary part of the project or when you're iterating on just a single feature you want to be able to have fast feedback not just sort of sit there waiting for things to boot or waiting for things to fail. So the goal for today is to go through each one of these and sort of describe sort of anti-patterns and best practices that I've developed over the past years that have helped give me confidence in a lot of excitement for adding feature tests. So readability. The biggest thing I can say for readability is the same thing that was said about Cucumber avoid any raw selectors and feature specs as much as possible especially CSS classes. I say especially CSS classes because I like to delineate CSS classes to just be purely used for styling. The biggest risk when sort of combining the two is that leveraging CSS classes either as a test hook and as a styling in use for styling is that you're eventually more likely to sort of overrun each other that if you have separate teams and a designer is changing CSS classes completely refactoring the markup if they change the CSS class they're going to accidentally break the tests and maybe in several different places. The other thing is the Capubar DSL works really well when you use proper HTML semantics and I'll go a bit more over that in a bit especially the form DSL. This means having labels with all your inputs properly marked up. Last is kind of the summary of the two of these is the user story described as something a user could actually read and make sense of. So to go over readability in the first case let's take a look at this quick feature test. So in here this is just a very simple sign in test that creates a user, visits the sign in page and then fills in the sign in form. But there's one thing I want to call out here is the fact that we're using the input IDs to sign in. I don't think this is very human readable. I don't think it makes sense from the perspective of a user story to have to look into the DOM, find the element that has the matching ID and then set the value. It's also a big part sort of thing to go with this is also not very accessible. So let's take a look at why that is. So if you look at the template for this this is a simple form for two inputs, email and password. We'll see that we're specifying label false and sort of a common pattern I've seen around a lot is to use placeholders in favor of labels. Not only is this making the future test sort of less readable maybe more brittle to changes in model naming, column naming which are less likely to happen but potentially so. It's also not very accessible so you're limiting yourself to like a number of users who may be depending on screen readers or other assistive devices to use your application. So maybe an easy way around this is just to instead of removing the label we'll hide it from view. Then the labels on the page, Cappy Bar can find the corresponding label with this text value that's associated with that input and our test passes. Great. Let me throw you a curve ball. Let's make this a JavaScript test. I'm actually using a JavaScript driver which is a more fully fledged browser under the hood that will behave more similarly to how a user's browser would. When we do this, the test fails and this is because Cappy Bar defaults since version 2.0 to ignoring any hidden elements on the page. Going back to the accessibility part that's how any users who are using a screen reader will also experience the page. They won't be able to find the input or hear a readable, understandable description of that input when they encounter it. So a nice alternative to this is using a special sort of CSS hack to make the label not appear on the page but still appear visible to browsers. So both screen readers and Cappy Bar and a JavaScript driver will both see it. This accessible hide class I get from is essentially these declarations and I get it from this SNCCS website where you discuss this various ways of making content hidden on a page that's still accessible to screen readers and I'll have my notes available later for people to review. So we do this in our test pass. And so you may be thinking, well, that was just a very simple kind of plain example but it gets significantly more complicated when you think about any applications that's using accept-snusted attributes or any other application that may not be built on rails that's using Cappy Bar and so forth. Sorry, not built on rails that's using Cappy Bar for testing. So even though this example is pretty simple, I have seen a lot worse examples where people develop helper methods in their tests to sort of generate the DOM ID that would be associated with an accept-snusted attribute's form input and it's just a complete mess because you can't really tell what's going on or where it is or what it's representing at some point and you're dealing with all this cognitive overhead of figuring out what this thing on the page is instead of just seeing, oh, that's the input for the expiration date. So next I wanted to move on to this other feature test. This is implementing a search for users in your contacts being able to search by birth date and it's using a jQuery calendar picker, so it requires JavaScript. I'm going to note that we're leaving out the functionality of actually interacting with the date picker for later. We will fill that out. I just wanted to, we'll slowly get there after we deal with some of the current issues with this feature test. But to get you on the same page just like what we're seeing on the page I took a little screenshot. So that's effectively the behavior that we'll be wanting to test. So back to our tests. We see we have these two sort of, based on our premises earlier, these two violations in our tests already. We're using a raw CSS class in order to find the button to open the calendar and manually click it. The other is we're using the sort of complex CISL CSS selector to assert about content on the page. While Capybara understands CISL it can sort of lead to significantly more overhead in terms of understanding the spec. You can do crazy things with CISL that you could also equally do with Capybara in a much more readable way. And your fellow developers and stakeholders will thank you. So let's tackle this first one. How do we make this easier? Is there any way to use a click button? Would this work if we passed the CSS class to a click button? Unfortunately not. If you dig deep enough you can find out what the allowed values are for a click button. You have to dig all the way down to the XPath gem that Capybara depends on which is how it builds all its queries for finding elements in the DOM. If you go all the way down you find there's an XPath HTML button method that takes a locator. And here you can see the locator can be any one of the button's ID, its value, or its title attribute. And so title attribute is a pretty simple semantic way to mark up a button if it has no implicit text in it. So what we can do with the markup for our date picker input is add title attributes to the buttons that are used to hide open and close or reset the calendar. So once we add those we can change our spec to read much more nicely, read much more fluently. Next let's try and break apart the sizzle selector. We're using the contains filter to check for search result elements that contain the word John in them. Well Capybara provides us an option for this already with any of the have CSS, have selector, and find methods you can pass a text option. And we'll get into it a bit more as to how you can find out what all those options are. So let's quickly just change that to use the text option. Great. Now we just have to deal with the way to get around the CSS class. So if you look at our markup what hook can we really use here other than either the tag name of the element or any CSS classes. Or just doing a slightly too vague like has content query which sort of can result in false positives because it checks the entire page. I'm going to propose a convention I have found to be very useful for you. Data attributes. According to the HTML spec, custom data attributes are inherited to store custom data private to the page and there are no appropriate attributes or elements. So this seems like a good fit. There's no really good test hook or existing HTML markup that's meaningful for us to use to find the element on the page to assert it's there or not there or properties about it. So let's add these two custom selectors. With Cappy Bar you can add your own custom selectors and define whatever X path or CSS you want to reuse for that selector name. So here we're adding two data attributes, data collection and data resource. So we can markup our content with sort of data resource equals search result and search the page using have select, you know find model element in the name of the element search result. Since that's slightly a little bit verbose, let's define some helpers around that. And then to make it even easier and much more sort of readable on our test list of metaprogram some other helpers on top of that. So effectively I'm defining, similar to sort of inspired by Cappy Bar I'm defining wrappers around these selectors it defines such as selectors for the select element, a field then defining methods to assert that element is there on the page. So this is the same as sort of select value from the label ID or other property of a select element and the same as saying the page should have the select label ID with the specific value. And then providing name methods for easier reuse just so the test are less verbose. So I add this data attribute to my markup and now I can update my test and I think this read significantly better than it did before. The benefits are we've decoupled CSS from our test hook. And we can also reuse these as hooks within our application in JavaScript as well. You can imagine sort of querying a collection of elements using data collection and filtering within that collection for data resource elements with the attribute data resource. So now it's filling the date picker sort of digging a bit deeper. That's not an intuitive thing to do with Capybara. This is what our date picker looks like. In order to interact with this, in order to change the date and pick one, we'll want to change the month and the year in the selects. And then from there, once it's re-rendered we want to click on the appropriate date in the right table cell. So let's take a look at the markup to see what we have to work with to see whether there's a label or ID or a name attribute that we could use to hook into the markup and interact with it in Capybara. So the header has two select elements. They have no ID, no name, and no labels associated with them. So the default DSL methods select blah from blah won't work for us. Additionally, the anchor tags disinclude a number. And the numbers aren't guaranteed to be unique because if we look back we can see that there are two ones in the calendar or two thirties in the calendar. So we can't be guaranteed that we'll be clicking the right one or which one should we click the first or last one. So now I'd like to sort of dig a bit deeper into Capybara's internals. Let's look at how it manages to build a really fluent DSL. So to introduce you, if you're not familiar with it already, Capybara element. So whenever you run a fine query what you're getting back in return is a Capybara element instance. It's a wrapper around the DOM element that you're representing and it provides you an interface for getting attributes or interacting with it in certain ways if it's a form input. And all the DSL methods we're familiar with are implemented in terms of these lower level primitives. So for instance, click button is defined just this way. It finds a button with the locator and the options you provide to it and clicks on it using Capybara element's click method which simulates a click based on the driver it's using. So let's use Capybara element to interact with the date picker. So for interacting with the year we'll check the CSS class. We see it's a select.ui-datepicker year option. We want to find the option within that select and use the select option method to tell its parent select I'm the one that's selected right now. We do the same for the month with a slightly different way to get the value in a different selector. And then for picking the date we find this required some digging into sort of what way I could uniquely query an anchor in the table. But we want the anchor tag with UI state default that isn't a UI priority secondary. This means it's a date in the calendar that's like the next month of the previous month. And then we match the first one that comes up. We match first because you can imagine getting an ambiguous match which Capybara will throw an error for if you're picking the first it will throw an ambiguous match because there's also the 10th, the 11th, the 12th and so forth. So now that we have these methods let's put them in our test and so now we've filled out that functionality for interacting with and filling out the datepicker. The problem with this is we're probably going to be doing this elsewhere in our tests. It's likely to be used in other forms it's not that great just to leave it in one place especially for how convoluted and specific it is to the jQuery datepicker. So let's extract a helper method and extract a helper module to go with that. So sorry, let me back up for a moment. What we're doing is we're extracting the choose date method which has a similar signature to the Capybara select method. It takes a time stamp and a from attribute to that should indicate the label of the or some text within the input that's associated with the datepicker. And then effectively we just extract all the methods all those calls into the module which we can include in our spec helper configuration. Last, I think it's good whenever you add sort of this custom functionality that you add an additional check to make sure it's working. In this case for our search we do a full page reload. We want to make sure that the value we entered is still on the page when the page reloads. So let's add an assertion that the page has a datepicker input with the right value that we initially input. And so I'm going down this path to sort of introduce another sort of lower level aspect of Capybara that can really help to clean up a lot of your tests and avoid using custom, avoid using hard coded selectors. To have datepicker input takes the label text finds the value, formats it in the way that's predetermined, and then looks for a field with the right label text with the right value that is disabled. Because since we're using a datepicker we don't want the user to accidentally type into the date field and change the date and submit an invalid value that won't parse. This was kind of a surprise for me. I just randomly tried this one day and it worked, and then started digging into where does this come from, what other options are there available. And this comes from Capybara selector. Capybara selector is what we added earlier when we defined our custom models element and model element selectors. It stores them internally and it has an entire DSL for being able to modify them with different options. So it's a little bit small, but you can see on that line it's defining the disabled option, defaults to false, it's boolean and how to evaluate it when evaluating the X path. So hooray, we're done. Time to party. Next I'd like to get into performance, sort of how do you make your tests a little bit faster, how do you get slightly quicker feedback. So a big way to get a lot of out of big performance when it's used the within helper. The within helper can scope multiple assertions to the same part of the DOM that you're working with or visiting. And it avoids searching through the same, so imagine you're making assertions about a model you've opened that's rendered at the bottom of the page. If you just make those same assertions over and over without scoping them, every call to interact with an input or start at the top of the document goes all the way down to look for it. So for example, we visit an address page, we expect the page to have 30 address elements using sort of the pattern we established earlier. We click a button to add address that opens a modal, each one of these calls starts at the very top of the page when our modal is at the bottom of the page. We can make it a bit more performant by adding the within helper so that each one of these calls starts with the modal instead of having to go past all 30 addresses and whatever else is at the top of the page. There is a small caveat to within. It can be somewhat of a gotcha sometimes. Imagine if you had a date picker input. The jQuery date picker input inserts itself at the end of just before the closing body tag. So if we're scoping within the modal dialogue and we try and choose the date, it's going to throw our element not found even though attempting to open the date picker input work, we can't interact with it because it's on a different part of the page and just positioned on the part of the page we're interacting with. Just mentioning this is a note in case anyone gets stuck, sees it working if they run it in Selenium and are just banging their head on why it's not finding it, it's just because you're within another element. Another big win for me, and I think also just in general for documentation especially how much larger apps is to write your feature specs as QA scripts. I think people bring a lot of sort of the unit testing methodology to feature tests where they don't really apply like one test for happy path, one test for sad path, and like another test for sad path, another test for this bug that we found. That's really expensive. Set up and tear down of every feature test can be pretty expensive especially if you have a large application and especially for Javascript test where it has to truncate the tables at the end of every test run. Additionally if you have to sign in or do a complicated process to get your user logged in and on the page they're interacting with, if you do that five times relative to the amount of time you're spending actually testing you may be spending more time doing setup for all those tests than you may actually be doing testing. This very tall bit of text is a single integration test from an application I've worked on before. It goes through and sort of tries to bang out all the functionality that's available on that page, all the edge cases for bugs to make sure that everything's covered in one run so I don't spend time creating user a dozen times visiting the sign in page dealing with that overhead to every test. For instance I have setup at the beginning I get on the page I want to go to I start making assertions about the object that's already on the page I see if I can edit it see if I can cancel editing it to make sure that it correctly updates the page I ensure within like the form to add something the right elements are disabled I make sure client validations are run I check that the form is updated correctly when I submit it. I'm sort of going through this long laundry list to sort of indicate like be liberal with what you do on the page and just make sure it's well documented and that's where a lot of these sort of earlier tips of making sure your testers are responsible come in handy because you can do these large form integration tests and have them be very easy to understand and if you have to just add code comments like this is really where to be doing in this test but it's to sort of check this weird edge case and I think that's perfectly fine so an advanced use of something really cool about Cappy Bar that I enjoy while working on some service oriented applications is that you can have the concept of a Cappy Bar window so you can be visiting two completely different apps within the same sort of Cappy Bar session this is great for testing at service oriented applications or maybe client set applications where you have the client set app in one repository or a different folder in the same repository than the server that's running it and the server doesn't even need to be Rails or anything Ruby based, none of it does and this is a quick example of this imagine I have a feature test you know the process that's running Cappy Bar isn't linked to either the client site application that is the API or the CMS that is the store management app I'm just running this as a process on its own there's been some set up process either on CI or locally on my machine to make sure those two processes are running configured such that I don't have to worry about these being hard coded or for demonstrative purposes in that way I can open windows to each of these applications using window open by you just sort of run some commands and it opens a window and passes you a handle to it then using within window you can interact with that window so in this test I'm visiting the CMS app I'm creating a product with a namefoo like most of these helpers are sort of just implementation details then within the store I'm visiting the base API and making sure that that product exists so this could be testing that from a CMS it can send in as an admin create a product enter its details and then all the process that's involved with moving it to another database and making it appear in the API and properly rendered in the UI that's all working with just like a couple lines of code and some initial set up then when I go back to the CMS app I mark the product in the stock go back to the store app and make sure that it's not visible anymore and so this is sort of an example of if your goal is SOA or client-side application with separation of concerns you can still use Cappy Bar to sort of make sure everything's coming together correctly and the nice thing is this can just all live on its own repository with its own configuration and set up is what we did on a project I worked on at a previous company and it was pretty useful to at least have that sort of documentation of everything so yeah so that's everything I have thank you for your time