Conf42 JavaScript 2022 - Online

Gaining Confidence with Cypress Tests

Video size:

Abstract

Have you ever wanted to refactor mercilessly but didn’t want to break the fragile tower? Or have you ever pushed to production only to spend the next few days cleaning up the regressions? You need integration tests, and Cypress is a great, fast way to build them. With a simple JavaScript or TypeScript interface, you can automate browsers to hit those critical functions in your app to prove it works as expected – this time and every time. Join us to dive into building Cypress tests and leave with confidence to refactor your way to greatness.

Summary

  • Comp 42 Javascript 2022 talks about gaining confidence with Cypress tests. AZ Givecamp brings volunteer developers together with charities to build free software. If you're in Phoenix, come join us for the next AZ give camp.
  • Cypress is a great way to do browsers based functional testing. It's built on top of mocha and chai. Different tools can be used for different types of tests. Ultimately, which tool you choose to do end to end tests is up to you.
  • Test using Todo MVC. The purpose of todo MVC is to create a client side MVC application in each framework. If ever you refactor your website and the pages move around, you want to make sure that you're hitting the actual page.
  • Use fixtures for mocking data. Use frequent commands for frequent tasks. Run cypress tests in production. Use these best practices on your website.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Jamaica real time feedback into the behavior of your distributed systems and observing changes exceptions errors in real time allows you to not only experiment with confidence, but respond instantly to get things working again. Those hi, welcome to Comp 42 Javascript 2022. Today we're going to talk about getting started with Cypress and in particular gaining confidence in our websites with Cypress tests. Here's the part where I tell you I am definitely going to post those slides on my site tonight. I've been chasing similar speakers and that's why the slides are available right now. Let's head off to roberich.org and robert.org and we'll click on presentations. And here's gaining confidence with Cypress tests the slides and the code up on GitHub are online right now. Achievements unlocked. While we're here on robrich.org, let's click on about me and we'll see some of the things that I've done recently. I'm a shoreline developer advocate. Shoreline makes it easy to debug and analyze cloud native infrastructure in the same way that you might analyze other infrastructure. I've gotten some awards from both Microsoft and Docker, and I'm a friend of Redgate. AZ Givecamp is really fun AZ Givecamp brings volunteer developers together with charities to build free software. We start building software Friday after work. Sunday afternoon, we deliver completed software to charities. Sleep is optional, caffeine provided. If you're in Phoenix, come join us for the next AZ give camp. Or if you'd like a give camp closer to you, hit me up on email or Twitter or find me here at the event and let's get a give camp in your neighborhood too. Some of the other things that I've done, I do a lot with JavaScript, so I got to work on the Gulp project in version two and version three. That was a lot of fun and one of the things I'm particularly proud of. I replied to a Net Rocks podcast episode. They read my comment on the air and they sent me a mug. Woohoo. So there's my claim to fame, my coveted net rocks mug. So let's dig into Cypress. Now, Cypress is great for testing a website, but I'm not quite sure if my website's going to work. So let me fire it up and see. I don't know. Will this talk go well? Let's see. It looks like it's going pretty well. I think my site might be behaving and it's nice that I get to learn this before my customers start emailing me telling me there's bad things happening on my website. Yep, I think this talk is going to go great. Awesome. Cypress Cypress is a great way to do browsers based functional testing. Or said differently, browser is a great way to do end to end testing of websites in the same way that a user would. It's built on top of mocha and chai, so if you're familiar with mocha or jest or jasmine, you'll feel right at home. Now, it's two parts. One part is the test runner, the electron app, and one part is a browser plugin that makes it work really well with other systems. And so, like many good stories, we started in the middle. Let's back up a little bit as we look at browser testing, there's lots of different categories. We might test a specific function. We might test a component. We might test a service, a web service. We might hit an API. We might test from here down. Now, each of these are various types of tests, and they'll need different tools. As we start looking at the tools, here's a good list. Now, as you grab these slides from robertsch.org, you can click on each of these blue links to learn more about that particular type of tool. Now, in this test, we're going to focus in this talk, we're going to focus on end to end tests. But as I'm testing different parts of my system, I will want to use different components to be able to get there. Double clicking into end to end tests. Selenium is the one that kind of invented this industry, and it's a great system for being able to test websites. Selenium uses Webdriver under the hood, and Webdriver has all but become a web standard. But selenium tests are kind of slow and kind of brittle. Selenium is just moving the mouse and waiting for the browser to update. So if the API isn't done yet, well, then I might increase the timeouts in my test that makes my test slower or I just rerun them, which means they're fragile, they're brittle. By comparison, Cypress has a browser plugin so it can watch the dom, and whenever those DOm is done updating, it can continue on with the tests. Cypress is deeply integrated into the browser, which means it works with specific browsers that they've already chosen to support. So if I need support for Safari or ie, yeah, Cypress really can't help us there. But for webkit based browsers, Chrome, Firefox, it works out great. Cypress feels like a jquery API, and that's a little dated, but we can kind of look through that. Test cafe is another tool here that might work out well. If you use Devexpress for other needs, then you might feel right at home. Devexpress uses CSS selectors, which works out really well. It feels really native. But Test Cafe has a browser context and a test context, so you'll end up shimming content between them. Similarly, you'll shim a lot of content across that marshalled boundary with playwright. Playwright is built on top of puppeteer. So if you've ever used puppeteer to remote control a chrome browser, then you'll feel right at home with playwright. The assertion syntax is not using mocha with chai or jest or jasmine, so it feels a little bit odd. But playwright has excellent support in lots of languages. Now, ultimately, which tool you choose to do end to end test, pardon me, is definitely up to you, but let's use Cypress here. How do we get started with Cypress? Well, the first step is we NPM install Cypress and then NPX Cypress Open. That will open up the ide. And we saw the ide a little bit before. It'll also scaffold but a bunch of example tests if we opt into doing that. So let's take a look at that IDE. Now, I've already done a NPX Cypress open, and so I have those Cypress IDE. I can choose which browser I want to run my tests in. In this case, I'll those chrome. It automatically discovers the other browsers I have installed, including electron, which is built in. It's a webkit based browser built into this ide. Now, I could choose to run all specs, or I could choose to run a specific spec. Here's the examples that it automatically scaffolded for me that show a lot of different capabilities of Cypress built into their kitchen sink website. But let's pop open this Todo MVC and we can take a look at how this test runs. Now, notice that it's running in a new edition of Chrome, not the edition of Chrome that I was running before. So any local cookies or session storage or browser plugins don't apply. And it can run really seamlessly inside this web experience. Now we can see that it is running the website as users would. That's great. As we click into each step, we can see the various tasks that it did and we can take a look at the details at that particular task. So it went looking for a new to do button and then it did it. And we can find the oh, there's the spot in the ide where it chose to click. Now, if we pop open the web developer tools, we can see that, well, it's just Chrome and so we can set breakpoints inside of our code and we can be able to see our functions running just fine inside this iDe. We can also use this to be able to help us pick stuff. We'll dig in more to how we can use best practices picking things, but that can help us form that Cy get command and we can choose to rerun our test that shows us that eight tests passed, no test failed, and how long it took to run these tests. But because it's just a regular browsers, if we pull up the console, we can see the console output at each step. So as we go between the steps it will actually update the console, showing us the details associated with that step. Excellent. So we saw in this case that all of our tests passed. And so I think our website is behaving as we expect. Let's take a look at the code of what produced this. So here's that website and we start out with Cypress JSOn. Cypress JSon identifies the plugins file. So let's go look at that plugins file. And this plugin file defines where all of the rest of the files are. In later versions of cypress, these two files are combined. Now I've chosen to put my results in a results folder so that I can specifically get ignore it. I have a fixtures folder, all of my tests, and that's inside test cypress fixtures. I have an integration folder and a support file. So here's all of my tests inside my integration folder and these are typescript tests. So in my root directory I have a TS config that tells me all the details of how I'm going to build my application. But I also have a TS config here in the cypress folder. Now this says it descends from the TS config above. So I don't need to redo all the settings, but I am going to set the types including cypress and include the cypress folder to be able to get out the typescript type functions. Now I chose in my package Json to do the cypress open and cypress run just so that I can have some really easy commands to run it. But we can also run in headless mode passing in the particular browsers that we want to run. So here's chrome, Firefox, and edge tests, and I'll run them all together to be able to run my integration test across all of the browsers that I need to run. So let's take a look at the test that we ran. Now starting at this test, this test is using Todo MVC. Now to do MVC is a little bit wanted now, but we can see how we can test sites that we own or sites that we don't own. The purpose of todo MVC is to create a client side MVC application in each framework. So there's examples in lots of different frameworks. Now I chose to implement the angularjs one. In this case we could choose to uncomment particular frameworks and run the tests in those frameworks instead. I'll go grab the site name, just that I can output that in my test block. And here's our tests. It should visit a page cy URL should equal the site URL. Now I chose to do this before each to go visit the URL so that I make sure that by the time my page loads, by the time my test starts, I already have the page loaded. Now that may not make sense in some tests when I want to be able to intercept things, but in this case it's perfect. Now I don't need to repeat that. At the top of every test we can see that we're grabbing site URL. So I just want to make sure that I've landed on the page that I expect. Now as I was first building this talk, they had the site based in HTTP and then they flipped it over to HTTPs. And so I'm really glad I had this test to validate that it was the page I was looking at. If ever you refactor your website and the pages move around, you want to make sure that you're hitting the actual page that you want to test. Next, let's start interacting with the page. So let me go get this to do list. Let me go grab all the list items in it, all the to do items and it should not exist. We can see that kind of jquery like experience where we have not exist. All else being equal, I wish there was just a not exist method like in mocha, like in chai or jest. But nope, they're strings. Okay, so now that we've selected and noticed that they don't exist, let's level up again and start interacting with the page. So I want to create a new to do. I'm going to create this write cypress test. So let me go find that new to do box. I will start typing in it and hit enter. Now we can see that those one grabbed that replacement expression to set that in place, but this enter doesn't have the dollar sign in front of it. This is just a magic word where we can specify the specific keys. So we could specify function keys, shift control, alt, any of the special keys that we need. We can use this syntax to get at. So I get that and I type in that box the new to do and I push enter. Notice that we're not awaiting anything. We didn't await the page cleaning either. Now once all the Dom events have finished, let's go grab that to do list and validate that it contains that new to do text and that our new to do box is blank. Let's level up again and start to type in a bunch of stuff. Let's create a new to do, create an irrelevant to do and then go click on the new to do to toggle it as completed. Notice that we reach into that to do to grab the children toggle and click it. That's really intuitive. Now let's go look for the to do list because we completed that, then we should have a class of completed so we can validate the CSS class as completed and we're ready to go. Should delete a to do. Let's go create two to dos. We will go find the new to do and we will click on that button. But this destroy button only shows up if we mouse over it. We're not running mouse events, we're running Javascript events. So there is no mouse over per se. So we're going to say force is true, we're going to click it even though it's not visible. And now our list should only have length one and it should be the irrelevant to do. We want to make sure that we clicked on the correct one. It should only have active tasks. So we've been doing a whole lot of this creating the to do, and that's, well, kind of messy. Wouldn't it be better if we could say to do add? This is a cypress command. So let's open up our commands folder and we'll build up a bunch of commands. Here's this to do add command. I'm going to pass in some text. I'm going to go find that to do button. I'll type in the text and push enter. And while we're there let's just validate that the box is now empty. Excellent. So now that we have this command we can just call to do add. Now because this is typescript, we also need to give it a typescript declaration file saying what this new command is. We can head out to the cypress docs and we can see how we can build this typescript declaration file for our new commands, but that makes our tests a whole lot more legible. If ever you've used page objects inside of selenium, this is exactly the same. So we'll add this to do, we'll add this to do. We'll add the new to do that we care about, and then we'll complete the new to do. Now, because we should only show active tasks, we should have two left. So let's click on the filter and then validate that we have two left. We should only show completed tasks. We'll do a similar thing, creating our to dos and validate that when we click the completed box, we only have length one, only the one that was completed. And for the sake of completeness, let's show clear tests. Well, let's clear the tests and validate that we don't have any left in our thing. So firing up this tests, we can see how we run each of those tests and start to level up through our experience as we go. We'll start by just validating the URL. Oh, here's the typescript build that it does, because it did a build. It actually refreshed the page and you can see how it rewrote the URL as well. So we visit the page, we have no to dos, we complete a task and all of our tests pass. Excellent. So let's level up a little bit and talk about this Hackernews PWA site. Now, this is kind of the spiritual successor to Todo NBC, the site we just saw. And this creates a hacker news client. Now, we want to be able to test this. We want to validate that, if we render this correctly. But we don't know what today's hacker news stories are, so it may not make sense to just do this. Let's take a look at how we might intercept requests. So here's a hacker news site. You can flip between the various instances. We'll start off by visiting the page straight away and validate that our site is as expected. Next, let's intercept a web request. We don't want it to reply with the actual hacker news content. We want to reply with our fixture. Now, we could use strings here. I chose to use regular expressions, but we want to very specifically use this hacker news fixture. Let's come into this fixture and we can see this hacker news fixture. Now, I've got some fake data here, and so I know that the title of this first article is, this is the first story. So as I go to render this component. I know that that should contain those specific story. I've taken control of that external resource and made it behave in a very predictable way. This is really helpful when validating if we can render our components correctly. Now, we could lie to ourselves and presume that the API always returns that way. But no, we want to be able to validate. It actually returns correctly too. So I know that it will return 30 stories on the page. So let me go run this and validate with the real API that it works as expected. If we only mock out all of our API requests, we won't be able to validate that our site works, only that our site works if we assume the API behaves that way. Now if this takes a really long time, we could name that thing and then wait for it. But in this case I think it'll run fast enough. So let's fire up this hacker news test and validate that we can hit our content and that we can replace content to be able to validate our controls, render successfully, and hit the real API to validate that our site still works even with real content. Yep, our site works just fine. That was cool. We were able to dig into our cypress tests and be able to run them in a really elegant way. Now, some best practices around cypress tests. It would be easy for us to just build up that XPath down into the particular element, but well, if we refactor our website then that will break. We want to make these tests a lot more durable. Look for an id or a class and hook onto those or even better, create a data datacy. And then in the test look for the attribute data cy that matches that element. The other benefit here is that we've very specifically detailed that our test that we have a test dependent on this code. If we were refactoring this code, then we may need to also refactor a test. Now does that mean that we might end up with test code in production? Why yes, but we can also run some cypress tests in production as well. Let's run a standard mechanism that hits our home page, goes to our shopping page, puts something in our cart and tries to check out. We can do that in production maybe once an hour, maybe once every 15 minutes. Now we're not going to complete those purchase, but we can get pretty close. That will ensure that if our site goes down, we know before our customers start calling and complaining. So maybe having our test content in production is actually a good thing. Another best practice, use these commands for frequent tasks. If you've ever used page methods inside selenium. It works really similarly and it can give you an extra layer of comfort there. It helps you focus on the execution of our test rather than all of the mayhem of how to get there. And if you ever refactor how you do it, you refactor it in one place and it'll apply to all the tests. Excellent. Now another best practice is to log in only once. If we start every test by logging into the website, our tests might be slow. Instead, let's log in once, save a token, and then use that token before each in all of the rest of our tests. Or even better, hit a test API that is able to grab a token and then save that for use in all of the tests. We do want to validate that our login works as expected, but not at the beginning of every test. That could make our tests low. Another best practice? Use fixtures for mocking data. If you have a consistent username and password or a consistent result set, put that in a fixture so that then it's not cluttering up the majority of your test and your test becomes a whole lot more legible and terse. Now we used a fixture in our application to reply to a web request. Instead of hitting that API, we got our fixture so that we could validate our control rendered correctly even if hacker news content changed. Now, Cypress is a great mechanism for being able to run end to end tests on our website, and I would invite you to get started. You can find these slides on robrich.org and hit me up in that spot where the conference is designated for live Q and A. Or if you're watching this on demand, hit me up@robrich.org or on Twitter at robrich. Thanks for joining us for comp 42 Javascript.
...

Rob Richardson

Developer Advocate @ Shoreline.io

Rob Richardson's LinkedIn account Rob Richardson's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways