Transcript from the "Using a Testing Library" Lesson
>> Marcy Sutton: So for unit testing I'm using Jest, and that's a test framework that's really common in react applications. I'm going to go write some tests, and the convention here is to use, I think it's double underscore tests double underscore and you put it in the component directory. So really close to the component, not off to the side in some standalone test directory.
[00:00:24] So the react testing library is a really great resource. So testing library. There's things like Firing Events, we're gonna use that. Using queries. So things like getBy, findBy, findAllBy, so think of like query select all, that kind of thing. You can get ByLabelText. There's a lot of really good utilities in here and they have examples of markup that you might be wanting to test and then how to use the Jest, in the testing library specifically, to go and do things, to assert things.
[00:01:03] The other one that I want to pull up, because I'm going to use it to write these tests is jest-dom. And this is some add-ons. They are custom Jest matchers to test the state of the DOM, and our little Gatsby application has these dependencies already installed, so you can look at the package JSON file to see what's in there, but these matchers are great.
[00:01:29] And the fact that they've, I think the Gatsby docs prompted me to install this, so I already had it, which was great. But there's things like to be disabled toBeEmpty toBeVisible, which would depend on CSS to be in there, toHaveClass, that one's really useful, toHaveFocus, and so one, toContainElement, there’s some really great matchers in here, and we’re going to use them to write some classes for our dropdown component.
[00:01:56] So I’m going to go over there. Close out of this tab list. I might keep our little change to the role to have two different examples. Actually let's pull out package JSON and see what we've got in here. So dev dependencies is usually where testing stuff lives, because I don't wanna ship it to ever user, like they don't need that.
[00:02:16] [LAUGH] This is just for development purposes. So I've got testing-library, I've got Cypress, so we're gonna use that a little later. I've got jest-dom, there’s some react findings in here. I've got the axe-core library that we could use, babel-jest to make sure we can write some modern code in our test to make it gets compiled to more, I guess for testing.
[00:02:40] You don't care about that as much, but it can be nice. We've got jest-transform-css. Yeah, there's lots of good stuff in here. So I'm going to go over to our components. So I've got these bad components. They don't have any tests, cuz they're bad components, so by definition they don't [LAUGH] have any tests.
[00:03:01] So in the test directory for the better components, that's the convention too, like I have kind of have a big collection of components in here, but sometimes you might group them by, like the type of component would have its own directory and its own test folder, and it's nice just to have the test and the thing you're testing in a similar area of your code base.
[00:03:25] So I've got this dropdown, and the things that I want to test are like the external API's definitely the start. That's more of a general testing thing, like passing in a specific name. I could test the number of items. I'm going to focus more on the accessibility things, and what we coded in here, and if I were doing this for real, I probably would have started with test driven development, to start with the test, but we're doing it the other way.
[00:03:53] That's okay too. So the button being focusable, I think that's something that would be worth testing, because it's sort of, it's part of this component that we want to make sure that the user can reach and operate. I want this to be an isolated unit test, so I think that's an okay thing to test.
[00:04:14] I don't have to write an integration test to capture that. I do want to check that if I interact with this button, and I can use that fire event to do it, I want to make sure that it opens this list. So some things I could check for, I could test for, this active class, that would work, but it kind of depends on the implementation.
[00:04:36] It's something they do recommend is adding some data attributes, data test IDs here, so I'm gonna add those. Something that I could use that might be less brutal than the active class would be, when this dropdown opens I want to test that focus is set to the first item, because that would assume that it's open, and it's visible, and that that focus stuff is handling.
[00:05:04] That was happening with us effect so the way it was coded means that that will only happen when the dropdown has been activated. So that's a way that I can try and write this test so that it's really focused on the outcome, and less on the implementation, because if that CSS class has to change, for some reason, surprise my test broke.
[00:05:25] So I think that's the approach I'm gonna go with for this one. I'm gonna keep this dropdown file handy, and the dropdown test. So I've got one test in here so far. This file imports react, and imports the testing library, which comes with a render method, and this fireEvent I could pull in.
[00:05:48] I import the component that I'm testing, so the dropdown in this case. I've got this one test so far just to check that if I pass in a prop, that this custom text ends up in the page that's working okay, cuz if it wasn't getting passed off, it just wouldn't be in the document.
>> Marcy Sutton: So something I did notice when I was working through this ahead of time was a bit of a gotcha that the button here is using a click event.
[00:06:37] Because it's a button, it can fire a click. It's not a div that required that key listener like we talked about to start the day. So that means that if I'm testing for something like on key up, like my fire event, that's not going to match, because even though I'm using the keyboard to do it, that's not how I bound my logic.
[00:06:55] So that affects how we read this test, and that ultimately would depend on what events you've added, so I'm gonna just copy some of this stuff, the dropdown just to get it on the page. I'm gonna give it another text just so that my things are different. So I've got this dropdown, I'm actually going to, I had these all written yesterday.
[00:07:31] I want to go look at the docs for a second and see what I'm going to use. So the queries for this, I'm going to probably use the same API to get it, so getByText is probably fine, and that will return the first matching node for query, so that means I can do some interesting assertions here.
[00:07:53] So I think getByText is what I want. GetByText, so, I don't care about the text though, I care about the node. I think the way that I did this was to add a data, so I'm going to add a data-testid, and I'm going to say activator, or how about dropdown, just, "dropdown-activator" to be very explicit.
[00:08:21] I'm also going to add one here on the list, so data-testid. I'm going to say "dropdown-itemList', just so that I have some handles from the testing. These will get stripped out once the page renders, so I don't need to worry about them like, they are data attributes so they'd probably be fine, but they won't end up in the rendered page.
[00:08:42] They're only for testing.
>> Marcy Sutton: Okay, so I've got this dropdown. I want to render, I think something we could look at is the render method for this. Where does that live? So Setup, render, not on that page, I'm just gonna search for it, cuz I wanna know what that API looks like.
[00:09:07] So render creates a test render instance, and I can pull out different things off of this. So the getByText is the first thing. I don't really care about the text. I've already tested that, I care about the node, so that's what I wanna get. I think I could probably pull out the container, that looks pretty good.
[00:09:33] We should rarely use the container, so maybe not. Debug, unmount.
>> Marcy Sutton: It's funny how they show an example, and then they're like, but you don't really wanna use that.
>> Speaker 2: [LAUGH]
>> Marcy Sutton: Maybe wrapper, pass a react component as the wrapper option to have it rendered around the inner element.
[00:09:52] Yeah, I don't really care about that. I just want a hold of the actual DOM node. So, here's an example where they've taken container, they're converting it to JSON. Let's just play with it, see what it does. Container. Maybe I think, I thought I could just say dropdown, and then just render it without pulling that stuff off, and then I could say, so I wanna get to the dropdown.getByTestId, so I want that ('dropdown-activator'), and then I really want to click that.
[00:10:39] So I can't really test its, I guess I could focus it, like check if, and maybe I do this like the activator could be const activator = dropdown.getByTestId, and then if I did activator.focus(), like try and call a focus event on it, and this is where it depends on your testing tools.
[00:11:11] Selenium web driver in Cyprus, for example, if you try to send a focus event to a div that isn't focusable, they will tell you that it's not focusable, but it does depend on the testing tool doing that, and so that's not always the case. Jest does I think.
[00:11:26] So I want to expect(activator.focused), that was one of those matchers, or was it is focused? ToBeFocused? ToBe-
>> Speaker 3: ToHaveFocus.
>> Marcy Sutton: ToHaveFocus, thank you. Activator).toHaveFocus