Testing Frontend
Testing Frontend
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:06] Let's talk about frontend testing. So frontend testing has always been a little bit controversial.
[00:07 - 00:24] There are some people who just think that it simply isn't worth the effort. Because whereas a backend or an API is meant for machine consumption, which makes it very easy to test, testing a UI is obviously very difficult because UIs are meant for human eyes and hands or fingers.
[00:25 - 00:37] So obviously it's a lot harder just as it is. I also feel like there's a slight sort of smugness sometimes to be detected, which is that people think that the back end is where the serious code is.
[00:38 - 00:43] That's what has access to the database or it can do the serious stuff. And the frontend is just sort of design presentation stuff.
[00:44 - 00:52] So it's not really that important. But I think that with the level of complexity that modern frontends typically have, that doesn't hold at all.
[00:53 - 01:02] I think testing frontend is important. So there's a library called Testing Library, which was created by a guy named Kent C.Dots.
[01:03 - 01:17] And I don't know if he's the one who came up with the idea, but definitely it's built on the idea that you use the accessibility features of the browser, which I think is genius because those are actually, that's machine readers and so on. So those are actually meant for machine consumption.
[01:18 - 01:32] And it makes it a lot easier to test things. And also it makes sure that you are sort of per default building your website in an accessible way, which is always good and you should definitely always be doing that.
[01:33 - 01:38] So we're going to be using Testing Library. We're going to be using it together with VTest again.
[01:39 - 01:51] And the last thing we need to use is something called JSDom because we're going to be simulating a browser. So let's install those three in our frontend folder.
[01:52 - 02:13] I'll be adding all three of them as developer dependencies. And then I will be adding a test script to our frontend package JSON.
[02:14 - 02:24] And that will just be the same as we've done before. So we sort of have four major items in our frontend code at this point.
[02:25 - 02:35] So we have the new booking component, which is the site, if you will. And inside of that, we will be showing either the Choose Type page or the Choose Date page.
[02:36 - 02:45] And there will be more pages later on. The last thing we have is the booking flow context and the use booking flow hook that we've created.
[02:46 - 02:59] So we could choose to test, for instance, the Choose Type page by marking the provider and then testing it in isolation. And if the complexity grows, that might be something I would want to do.
[03:00 - 03:18] So I'm not recommending against it, but I think with what we have here, it's fine for us to test the entire new booking component on its own and with everything that 's entailed inside of that. So the question is now whether we should go end to end or whether this should be an integration test as well.
[03:19 - 03:35] And since I argued in favor of connecting to the database in our backend tests, you might expect that I would argue in favor of connecting to the backend from our front end tests. But I actually find that that is not completely necessary at least with what we have here.
[03:36 - 03:52] That is not to say that you shouldn't at some point because there are a lot of situations where that is the only right way to go. But I do feel that since we have type safety here, we're protected from a large share of the potential problems that would come from calling the backend.
[03:53 - 04:07] So I think it's perfectly fine to mark the backend for our frontend tests. That makes them significantly faster to execute and also a little bit easier to write as we can mark data to look the way we want it to.
[04:08 - 04:23] So we're going to be marking the TRPC backend and there are a few ways we can do that. On a previous project, I actually marked things manually by monkey patching the React query instance.
[04:24 - 04:32] But I think we can do something more elegant. So there's a very popular library called MSW, which is short for mock service worker.
[04:33 - 04:45] So as the name implies, it actually creates a service worker. So it's a little bit different from a completely plain mock in that you actually go through the HTTP library to make a request.
[04:46 - 04:56] And then the browser will just call its service worker instead of the backend to respond to your request. So this gets you sort of closer to what a real life scenario would look like.
[04:57 - 05:08] And somebody has been kind enough to create an MSW TRPC provider or plug in or whatever it's called. And I should note here that this is a young project.
[05:09 - 05:18] So maybe this is a little bit of a gamble because it might be that this person turns out not to maintain it. It seems to work fine for now and I expect that it will grow.
[05:19 - 05:26] So it's probably fine. And even if it doesn't, I feel that the risk is not too bad because this is only for testing.
[05:27 - 05:40] And it's quite easy to replace it with a different mock if you need to at some point in the future. So let's install this and we'll also install the MSW library itself because that is required.
[05:41 - 06:05] And then something called cross fetch, which is just a polyfill for the fetch that is provided in browsers normally. So we need to have access to that in our node environment when running tests.
[06:06 - 06:13] There we go. So now we need to make sure that this fetch, the cross fetch polyfill is being used.
[06:14 - 06:22] And the way we do that is by creating, let's create a test helper's folder here as well. And we are going to create a setup file.
[06:23 - 06:31] And what it does is it imports fetch from cross fetch and sets global.fetch to be that instance. So now we have polyfilled fetched.
[06:32 - 06:39] It's important to note here that this is a setup rather than a global setup function. And I'll get back to that in just a second.
[06:40 - 06:49] But let's just create out here a vtest.config.ts file as well. And let me show you what this contains.
[06:50 - 06:58] So it looks a lot like our backend vtest.config but with a few differences. So where in the backend we were using a global setup.
[06:59 - 07:03] Here we're just using a setup file. And this is an array but we're only using one.
[07:04 - 07:14] And this is called setup.ts. And the difference is important because the global setup is being called once for all of the tests but also it's being called in an isolated scope.
[07:15 - 07:26] So if we did this in the global setup it would actually make no difference. Our tests would still not be able to see this polyfill because they run in a different context.
[07:27 - 07:34] So it's important that this is a setup file. So this will be run for each individual test or at least test file.
[07:35 - 07:40] Finally, we specify that we want to use the environment JSTOM. So this is the simulation of the browser.
[07:41 - 07:46] So obviously tests run in a node environment. We don't spin up an actual browser for these.
[07:47 - 07:59] We'll be doing that for end-to-end test but these tests are just running in nodes. So we need to simulate the browser and JSTOM is still what I prefer to use for that.
[08:00 - 08:14] Let me also quickly explain why we need to set sequence hooks to stack. So the reason why this matters is that per default, vtest is going to run the before all and after all calls in parallel.
[08:15 - 08:25] So the order of things is undefined. By setting this to stack it could also be list but stack will mean that they will be called in a first in last out order.
[08:26 - 08:46] And that's important if you're using the pattern that we've been using with test hooks because if you create one test hook that say provides the database connection and then you have another test hook below it that relies on this database connection. You want to make sure that the database connection isn't destroyed before your second hook does whatever cleanup it needs to do.
[08:47 - 09:08] So for that reason it needs to be called in a stack order and for that reason I always set this as one of the first things I do when I set up vtest. So speaking of test hooks, let's encapsulate all of this TRPC marking into one so that we have a bit of code that we can easily reuse for all of our front end tests.
[09:09 - 09:23] We'll create a new file here called use mock router that will contain this and let me just quickly go over this. So this is the hook and similar to before it calls up before all and then after all call.
[09:24 - 09:39] So what it does is it sets up a server or it creates something called a setup server. This is all a MSW lingo so it creates this setup server instance first that will create the server for us.
[09:40 - 09:52] So in the before all call we do that and then we call listen on it. In the after all call we simply call close on it and then this hook returns this closure that returns the server itself.
[09:53 - 10:01] So we will now have access to the server in the describe block where we are using this. Let's try and create a test for it.
[10:02 - 10:21] So if we create a test for a new booking out here. .tsx and let me just copy some code here.
[10:22 - 10:32] Use mock router is what I called it before better keep to that and then we should change it in there. I already called it that there.
[10:33 - 10:44] I hope that this can discover it now. Let me just try and restart TS to make sure that that is not what's going on here.
[10:45 - 10:57] It is restarted and now everything works. I'm not sure why TypeScript sometimes does that but I'm sure if you're working with it you're used to this well by now.
[10:58 - 11:27] So what we're testing here is this new booking component which will start by showing us the first page in this flow which is the choose type page. So we're using our mock router here and we're saying that the get service types end point should respond with a 200 so an HTTP okay and the data should be what's in here which is an array which has one object and that returns our test service type with this description.
[11:28 - 11:37] So what we're doing down here in our actual test. So this test is in this describe block where this mock router is now activated.
[11:38 - 12:20] So in here we're calling the render which is part of the testing library and we 're rendering providers which is what we had so that we have our context in place and we might want to change this for test providers at some point but for now this real one works and then we're just rendering the new booking in there and that will automatically go in and activate the first page and it will show us the choose type page which should fetch the first service type and then it should show it in this unordered list here. So our test will await until the text test service type is to be found on the screen.
[12:21 - 12:27] So that is the name here that should be shown in the unordered list. So as soon as that appears on the screen this test succeeds.
[12:28 - 12:35] Let's try and run our test here. There we go.
This lesson preview is part of the Fullstack Typescript with TailwindCSS and tRPC Using Modern Features of PostgreSQL course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack Typescript with TailwindCSS and tRPC Using Modern Features of PostgreSQL, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
![Thumbnail for the \newline course Fullstack Typescript with TailwindCSS and tRPC Using Modern Features of PostgreSQL](/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fassets.fullstack.io%2Fn%2F20240619184857865_KristianDupont_FullstackTypeScriptwithTailwindCSSandtRPCusingModernFeaturesofPostgreSQL_1450.67x816_CoverImage%2520%25281%2529.png&w=3840&q=75)