Check out a free preview of the full Enterprise UI Development: Testing & Code Quality course:
The "Abstracting Rendering & User Events" Lesson is part of the full, Enterprise UI Development: Testing & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:

Steve introduces the user-event package from Testing Library, which provides more accurate user event behavior. Duplicating the user setup and rendering in each test can become redundant, so an abstracted render method is created in the utilities file, and the test is refactored.

Get Unlimited Access Now

Transcript from the "Abstracting Rendering & User Events" Lesson

[00:00:00]
>> This is most of the way there. There's really like one nuance here, yeah.
>> I'm curious, so the fire event.click.
>> Yes.
>> Can you use a standard click event? Could you get the DOM object and do a .click on that?
>> That is effectively what fire event is doing.

[00:00:17]
>> Okay,.
>> Right it is firing a click event, right?
>> So, but can we do, I mean, could we do the button element .click directly, or is that not working in this context?
>> I don't think that one works, but we can find out. No.
>> Okay, so it is something about the context.

[00:00:43]
>> Yep, but you did lead me to a better question which is, okay, maybe with click events, but there are a whole bunch of other events either a, are very hard to simulate by doing this. Or b, is not the way that a user really uses the DOM, right?

[00:01:05] For instance, we might choose to register an event handler at onkeyup, right? But a user does not just fire a keyup, right? A keyup event is when you take your finger off the key. I don't know, humor me for a moment. What things would have to happen for a user to lift their finger off the key?

[00:01:36]
>> Key down.
>> Key down, right? [LAUGH] Yeah and there's some amount of holding that's happening at that point, right? So generally speaking, the whole reason we're doing this was we want to simulate something that somewhat resembles reality, then this is, yeah, it's fine, but it's not great.

[00:02:01] And so there is a library that is spiritually, when I say spiritually, I mean in the same NPM org related, which is called user event, right? And we have it here and you do need to do a few things, which is, before you use it, you have to do, I think that's just user, cool.

[00:02:36] An user event seeks to create heuristics that are more natural. So you do not fire a keyup event but you could theoretically say, it would simulate the act of pressing the M key down and taking your finger off. Which is probably gonna give you better and more accurate simulations of what is actually gonna happen.

[00:03:02] Cuz again, testing is great, awesome, wonderful, do it, yeah. You know what's worse than not having tests? Having tests that are just inaccurate enough to give you a false sense of confidence that isn't true, [LAUGH] right? And so, yeah. And one thing that you'll see is, so we can do user., and here's a bunch of the different options.

[00:03:30] At one point somewhere in the notes, I link, I have a list of all the things that you can do. Now, importantly, I'm pretty sure I opened up the type file and just copied all the keys out, but you can also see it in here. I think kind of one of the interesting things is, even for instance keyboard, you have a string, it will simulate the act of typing that string.

[00:03:58] So, and on change, if you've done react, you probably had an unchanged. But theoretically, no user triggers an on change event, they type into the input field one letter at a time, right? And so this will simulate a little bit more of that. And you can see there's a whole bunch, there's upload, yeah, I have a list somewhere in the notes for you as well.

[00:04:25] And so we can do user and we can do, this one click is alternatively the same. And that, it's gonna leave that IntelliSense as I'm building up to something, I'm just gonna let it sit there for a second. Don't don't look at it, don't look after the colon.

[00:04:41] User, click and we'll click on the button, which I should definitely call an increment button. And my test fails, great, really glad I went to that whole spiel. Everything's good. Wait, right, careful listeners will see that in the act of doing multiple things beyond just finding one event that triggers something in this whole pressing a key down stuff along those lines, that takes time, which means we return a promise that will settle when it is done doing the thing.

[00:05:13] And we do need to await that, in this case, a click, sure, but a type or something along those lines and we can begin to have that in place. The other thing is that this should have gone into setup, which is what I aspire to run at the very beginning.

[00:05:34] This will also extend it as well. This is really what I wanted anyway. Utilities is a place for me to put helper methods, I now know this because I'm ready to put a helper method. So let me go and wind that back a little bit, we'll just change this.

[00:05:48] As you've just learned, these file names are completely arbitrary, which is nice. But, to the theme that we had with the GitHub Action before, I don't like to repeat myself, and you know what seems not fun? Having to put this at the beginning of every single test where I need to simulate a user event.

[00:06:11] Because we can assume, maybe assume is a strong word, that if one was going to go ahead and render, they are likely, if they're not just querying the DOM, at which point like, we can argue that to match that snapshot is better at that point, I still say no.

[00:06:26] But, the fact that these things will probably come together fairly often, is that fair? So maybe, if I know that I'm gonna use this, maybe if I go through the act of rendering, maybe you just also do this, right? And so let's give ourselves an abstraction here, which is, we're gonna pull in render from testing library react.

[00:06:51] In fact, let's actually we're gonna pull in everything. Yeah, we'll just do this for now. Render, And you have two choices how you do this, I'm going to do it, I'll decide on the moment. We'll do const, call it render component for a second, because yeah, can't have the same name.

[00:07:19] Which should take, if you look at render, it actually takes two arguments, which is borderline impossible to read, but you squint closely. The first argument is that element like we saw with counter, and the second one is some number of options, right? That one's pretty easy to type.

[00:07:42] The other one, render options with the generic of queue, and I think it also then takes out queries, I've looked at this before. So here we'll say, React.ReactElement, and the options. What is options? We'll deal with that in a second. I don't wanna do this, I'll do it for a heartbeat.

[00:08:10] And all that we wanna do in this case is when we say renderComponent with a component and all of those options, what I would like you to do is go ahead, in fact, We'll call it ui. We'll keep it the same as what they call it. Const result, we can refactor, I'm live coding it.

[00:08:39] Where we will, in fact, render that ui with those options. We will also do that userEvent, no, go away, .setup Same thing that we had from that other file, and then we will return everything in result plus the user. So now when I call renderComponent, I will call render and also call userEvent.setup at the same time and I'll get both for free.

[00:09:20] We'll refactor that in a second, this is actually just a top level export. I'll refactor in a second, but we can go ahead and call this one renderComponent. If you're like no, no, no, I don't wanna call renderComponent, I wanna call it render, you have a few options where you can handle this.

[00:09:40] You can say as, Make this one renderComponent that you'll never use again. And this one can be render, right? Yeah, it's like they just can't be called the same thing at the same time. Now you will fight with IntelliSense later over this, but I am willing to have that fight and do it.

[00:10:01] So we still gotta fix this. There's a few more things that we need to fix as well, but let's go back into our test. I do have the counter svelt there, so you can see both. All right, cool, so here we've got, now what we can do is let's get rid of this render for a heartbeat.

[00:10:19] And we'll have render and you can see my one from this test utilities in this folder. What you angry about? So it gives the user the container, the base of the here's all the things. What you upset? Why are you mad? Expected two arguments and got one, that technically makes sense, that is the code that I wrote.

[00:10:54] So we can fix that, because it doesn't know that that one is optional. So we could say, and now it works, right? I can refactor this a bit. No, don't say that. Don't save any random things I wrote in any file. Okay, so we can refactor this a little bit here as well, which is this any will, one, opt me out of any kind of sense of IntelliSense.

[00:11:28] So a few options here, which is I could do, There isn't a type that I can just export right now, as a moment that you all are sitting in the room and whoever's watching us as I record this, there's no type that you can import for that option is because it's dynamically.

[00:11:47] So I could do, get me the parameters, typeof. So, zero base, so this option should be whatever the second argument to the render from React testing library is. I don't know what that is, but that should be the same thing, right? And that will do the trick. The only things that I would do here to make this a little bit easier, I did these to show you everything that we get from the results spread out in an object plus the user, we could shorten this a little bit, now that I've made my point.

[00:12:38] Cool, all my tests pass. So now I have a special version of render that automatically also sets up the user events and gives me that user. So let's go ahead and just change this real quick. And we'll go ahead, now I don't need this. And we say const user, And now I get both for free at the same time.

[00:13:06] And life is good and there was much rejoicing everywhere to be found.