Web App Testing & Tools

Multiple Fetch Requests in One Test

Miško Hevery

Miško Hevery

Qwik Creator (Previously Angular)
Web App Testing & Tools

Check out a free preview of the full Web App Testing & Tools course

The "Multiple Fetch Requests in One Test" Lesson is part of the full, Web App Testing & Tools course featured in this preview video. Here's what you'd learn in this lesson:

Miško creates a test that requires two asynchronous calls to be sent. Mocks are used for each request and a test is written to ensure the result has the expected shape. The final code can be found on the lesson-4 branch.


Transcript from the "Multiple Fetch Requests in One Test" Lesson

>> So now, let's do something more complicated. So let's describe getRepositories. Okay, this is plural, right? GetRepository was getting one. This is a plural, which is we have to do more than one. And I think I wanna show off. All repositories for a user. And the thing that needs to happen is, we need to do pagination, right?

We need to be able to talk to multiple fetch requests inside of a single test, because the first page gets you x number of Items, and so on. The thing we want is we wanna say const responsePromise = api.getRepositories for username, right? And we wanna expect that the fetch mark to have been called with, it's gonna go to a users/ USERNAME.

Is that the correct one? So it's correct. But here, it should say per page 30. So I want 30 repos per page, and we need pages =1. So we're doing pagination, right? We're gonna have other things coming through. Now, because we already tested that headers are correctly done.

I don't wanna keep repeating myself here and doing that. So you can say expect any object here, basically, we're saying. You know what, I really don't care what's gonna come here, because that's not really part of the test. We already proved to ourselves that we can do this.

Let's not keep repeating ourselves because in the future who wanted to change this, you don't wanna be changing this thing in 50 different locations. And here, we're basically saying, look, this is not important over here. I know that some things have to be done over here, it's not really done That important.

And of course, we need to then say fetchMock. So we wanna resolve this thing. Now, what we're gonna do is we're gonna get an array here, right? And so we're gonna, oops, I had this right. Let's push again. So we're resolve new Response. JSON.stringify. And so we're gonna have a bunch of repos in here, but those repos, we're gonna build up in a second here.

RepoSet 1, And we're gonna fill in the details of it here later. So we're gonna get one set of repos here. And then, what we're gonna expect is that we will have another one of these calls. And this time, it's gonna ask for page two. And then, we will have to respond with repoSet 2, something like this.

And finally, we want to be able to assert that if I await response, then it returns basically both of these sets in here. Now, to create this, how do we know that we need to go further? We know we need to go further because we are gonna have 30 items in an array.

And so it knows that if it gets 30 items because it has page size of 30, then there is a next page, right? So if we just repo this, so we fill this with. Okay, let's do it as a map. Map, which gets a index and returns a id, let's say 1.

And then, the second repo, we will just return single one id s 31, right? And so then, we assert that we have received both of these parts, and we're sending it as string five versions, and we get it back as the string five. So we wrote a test, right?

And this notice this is TDD, right? We kind of described what we want ahead of time. And because we're describing what we want ahead of time, it's pretty easy to make the test look neat. What you can get yourself into trouble with is if you write the code first, then it's very difficult then afterwards to write the test because you don't have the right hooks.

But because we wrote the test first, it's gonna be really easy for us to go and implement what we wanted. It's pretty straightforward. I would imagine, right?
>> So I have a question actually. So in this case, you add the fetch and delay function as an argument to your GitHub, API, right?

>> Mm-hm.
>> And the reason for that is because you wrote a test that want mock those particular arguments, right? Well, a lot of times, you also want to abstract those details away from the user. You want to high fetch, and also delay. So how do you balance that?

Do you just write your code based on your test that way? Yeah, so that's a good question. You would probably create some kind of a factory for it. So there's a couple of ways to do it. So here, what we could do is we could make sure that all of these have a default value.

Let me kinda show this. I think if I just say, fetch here, which is a default implementing, right? And I say delay here. I'm pretty sure this is gonna fail. No, it's passing, actually, look at that, it passes. Okay, so you create your constructor with defaults set up such that anybody can just instantiate and get the correct things in there.

That's one way to do it. The other way is to have some other object responsible for creation, and then you can then ask it to give it to you as a production mode, or inside of a test mode, right? So that instead of going directly inside your code base and instantiating one of these things, you could go through a slight level of indirection and say, okay, have this builder give me what I want, and then you know that this thing is already configured in the way you want.

And I think this is important because if you're doing something in production, for example, this has to be configured with the current token, right? And that's a security thing that you don't wanna check into your code base. So how do you set up the system so that you can easily get a hold of this API, but at the same time configure with the correct token already.

So again, you probably wanna have kind of a additional third object responsible for, for building these things up for you. Right, so now, that I have this, we can go in. And we can use Copilot to do its magic. So let's see, declare method getRepositories. And here it is.

Let's give it the username. And so now,, we would like to just make an implementation. So right now, it says it just throws an error. And so that's what the error one over here says, like this particular. Test failed because you don't have an implementation in here, so let's implement it.

So it's gonna be async, so we are gonna be in the loop, right? So, and then what we will do is we're gonna keep fetching stuff. Let's see, what is this? Is this correct? Let page equals 1, let's see. So we're in the loop, we keep fetching it as long as the JSON length is 30.

As long as we have things of just trying to break it, I see. Why we can break it, that's fine. As long as it's less than 30. Repositories push. Okay, so this is gonna be true. And we need to declare repositories. Costn repositories, look at Okay so,
>> Because we didnt do the second object.

So we probably wanna reuse this code. I'm just gonna copy it. No, not like that. It's gonna be headers. Okay, why are you not happy? In my thing, I never had the second expectation. I just had the resolution. Yeah, and this one has to be 1. So what is this?

>> I is actually the second parameter in map.
>> Yeah, underscore I
>> Underscore i, yeah, right?
>> But even then, why am I getting all this nulls here? It's like this map doesn't run.
>> You have to fill it with null. Now it has the right objects, OK.

We don't have the 31st object. That's because, right, we exit too early. So we need to move this piece of code above. Look at that. We found a bug in our implementation. There we go, okay. But notice we found a bug, as in the copilot generated this push.

It exited before it pushed it in there, right? Interesting that you would just get this particular bug. The main thing I wanna show off, though, is how you basically compose these things and how you can get bigger and bigger things in here.
>> Testing asynchronous code is difficult.

>> Testing asynchronous code is difficult, but I wanna point out just how useful it is to have these marks. Because with marks you get to control when things resolve. When you control when things resolve, you can do all these things. By the way, I think I have to add this delay here.

Yeah, it fails without this delay. This is an example for it, right? We resolve this particular promise, but because we don't yield to the skill Scheduler, our test blocks, the schedule from running. And so await delay(0) is a common trick to basically just yield to the schedule, so that the other promises can run.

So when we come back, we can go and assert other things.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now