Designing Effective Tests with React Testing Library

Rate this content
Bookmark
Slides

React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?


In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.


Prerequisites:

- Familiarity with building applications with React

- Basic experience writing automated tests with Jest or another unit testing framework

- You do not need any experience with React Testing Library

- Machine setup: Node LTS, Yarn

131 min
07 Dec, 2022

AI Generated Video Summary

This workshop on designing effective tests with React Testing Library covers topics such as testing the contract and rendered UI, using the Screen API for text assertions, testing environment and accessibility, the benefits of test-driven development, choosing the right query method, confirming presence and absence of elements, testing external functions and mock functions, organizing tests and testing effects, mocking web service requests, testing asynchronous code, and testing the contract and conclusion.

1. Introduction to React Testing Library

Short description:

Welcome to the workshop on designing effective tests with React testing library. We'll be together for about three hours, covering innovation slides, live coding, and exercises. I'm Josh Justice, also known as CodingItWrong. I've shared React and React Native testing content at various events and conferences. Testing is my passion, and I'm excited to share my knowledge with you.

Welcome, everyone. This is designing effective tests with React testing library. We're going to be together for about three hours working on this. I'm going to be sharing some innovation slides, going to be doing some live coding and giving you some exercises to work through as well.

So, again, as I said, just a minute ago in the lead up, codingwrong.com. It's down in the footer of all the slides. As we go along, you'll see it if you need to pull it back up. There's information about everything we're going through. Links, ways to stay in touch and the link to download the exercises repository.

So, a bit about myself. My name is Josh Justice. I go by CodingItWrong online. And you can decide if you think I'm coding it wrong. It's up to you. So, I've gotten a chance to share React and react Dev testing content a few times. I got to give workshops similar to this one at React Advance London this year. I good the organizers for giving me a chance to connect with you all here and there. Chain React, last time it was held at a React Native US conference. I got to do a workshop on React Native testing. And I'm doing the same one again this next year. In 2023 when Chain React is finally back. I give a talk on test driven development at React Native EU as well. So, React React Native Circles. I move around here and testing, in case you can't tell, is my passion. It's really transformed my software development experience. I like to equip others with helpful things about testing as well.

2. Testing Philosophy and Approach

Short description:

Social media is a little strained these days, isn't it? I'm not on Twitter. More people are using Mastodon. I've written a book about React testing. I work at a consultancy called Test Double. We feel like software is broken in general. We're going to cover react testing library, the user event library, and just DOM. We're specifically focused on component tests. The workshop web page will continue to be available. Feel free to ask questions in the conference's discord or my own discord community. You don't have to agree with me. We're ready to jump in. This workshop assumes you're familiar with React. React Testing Library is the library we're going to use. The term component test is a test of a component. React Testing Library is probably the highest adoption as far as React Testing Libraries.

Social media is a little strained these days, isn't it? The last couple of weeks? Oh, OK, that crashed again. My apps are also strained. I'm just going to do this in a simple way. So, I'm not on Twitter. I haven't been on Twitter for a while now, for over a year. So, more people are using Mastodon. If you've tried Mastodon or you want to check it out, as far as this other social network. I'm coding it wrong at TDD.social. But you can find me there. I'm linked there from my page as well. Good luck as we all navigate social media together in the weeks and months to come.

I've actually written a book about React testing as well. It's on test-driven development. So, we're going to be talking about testing in general today. And if you like it, if you find what I have to say helpful, test-driven development is a way to approach testing that actually really aligns very well with the philosophy of testing we're going to talk about today. And so, you can check it out. The intro chapters are actually free available. You can download, so you can check that out. I have a $10 off code for folks in this workshop. You can just use the link here. And the book is also available on Amazon paperback if you prefer paperback. But yeah, feel free to check it out if you would like.

I work at a consultancy called Test Double. I didn't choose it just because testing isn't a name, but that did help. We're a company that improves the world's software. We feel like software is broken in general at large. Maybe you feel that way too. And we want to try to help fix it. So if we could help you by joining your team, embedding some developers with you, me or someone else, similar views to me or different views from me. We'd love to join and help you out we work on React, React Native, many other web technologies. So you can go to testdouble.comagency to find out more about us. We also have a newsletter. Again, not knowing where social media is going, and we have had a newsletter. And so email addresses will probably keep existing. And so if you'd like to hear more content about react and testing, and other software development principles, feel free to sign up at testdouble.com newsletter. We take privacy and people's data very seriously so your email will not go anywhere bad. It's just going to give you this newsletter until if you decide to unsubscribe. So feel free to check it out.

What we're going to cover together today is react testing library, the user event library that's closely associated and used for user action, and just DOM which is some just testing tools that make our assertions a bit easier to write. So we are specifically focused on component tests with react testing library, not so much end to end testing. Afterwards, this the workshop web page is going to continue to be available indefinitely. So you can always pull that back up to learn more. So the conference has a discord. And so I only pause to talk about that. As we go along, feel free to ask questions. If you would like to unmute and ask a question, totally fine. If you like to talk in zoom chat, you can ask questions in there. I do have discord chat pulled up in get nation's discord for React and that's a great place. That's maybe the preferred place to ask over zoom chat because that way those questions are available for people watching the workshop later. Whereas the zoom ones will not be available. Either one is fine but if you are logged into discord, asking in the workshop thread there could be great. So after the conference, I'm going to continue to be in the conference's discord so you can reach me there if you like. I have my own discord community as well. Folks that like to talk about React and React native and testing and test-driven development in those areas. So the link is on the workshop webpage. You can check that out if you like. There's a number of folks in there helping each other out on testing things and edge cases that I haven't run across yet. It's a fun place to be. Something else I want to say is, I don't know, you can decide how opinionated I am in this workshop. I have opinions, fairly strong at times, on testing but I want to make sure I communicate that you need to decide for yourself. You don't have to agree with me. That's totally fine. If I get you thinking about something and you decide you think differently on it, but it's prompted some thought, that's a win in my book. I really deeply believe that people and teams and products and organizations are different. And so your needs for testing may be different than what I'm describing here. So I hope it's helpful raw material for you to evaluate. But please feel free to welcome to make your own decisions and have a learning mindset.

Alright, with that, we're ready to jump in. And as we go, we're going to take, because we're stopping three times for exercises over the course of the workshop, that will be a natural time that you can take a break as well if you need something to drink or a snack or something like that. For the second one, we'll take a bit of a longer time just to make sure people have time to take a break. But we'll be going through slides, me demo and live coding and these exercises together. Alright, so with that, let's get started. Starting out first talking about just rendering basics when it comes to testing, one other thing I should say in introduction is, if you have this workshop assumes you're familiar with React, that I've written software in React before, you don't need to be familiar with React Testing Library. So, if you've never written a component test, you'll have information here to equip you with the basics of how to do it. If you have written with React Testing Library as well, I'm also sharing a philosophy of how to think about and how to structure tests. And so, if you know how to do it technically but sometimes wonder what tests to write or have kind of challenges with your tests, maybe that will be helpful. And third, maybe you have already have your views on how you like to write tests and you feel like it's going smoothly. But maybe that some of the concepts that I share help you to give you words and languages and ideas for you to have conversations with others as you work with your team to decide on testing approach or maybe level up newer developers together. So, whichever of those buckets you fall into, I think you'll find helpful information from our time together here.

What is a component test is where we start out. Testing terminology, there are so many different terms. Just if you want to start an argument, just ask some developers what a unit is in a unit test and watch the fireworks. So, I'm going to define the term of what we're talking about today. The term component test is a bit more concrete because, you know, when React, we have components. And so, the component test is a test of that. React Testing Library is the library we're going to use for component testing today. So, if in your test, if you are rendering a component, you're specifying a component, usually with JSX in React, that's a component test, most likely. React Testing Library is probably the highest adoption as far as React Testing Libraries, component testing libraries. There's some other alternatives. Cypress has recently released component testing as well.

3. Testing Philosophy Across Frameworks

Short description:

React Testing Library is specifically what we're going to be using. The npm package for it is at testinglibrary slash react. The website is testing-library.com slash react. And testing library is a suite of testing tools. There's some for just the web in general. There's some for many other different frameworks, Angular, Svelte, things like that. But React Testing Library as React has a high level of adoption, React Testing Library does as well.

And it actually has some really great tradeoffs. I love Cypress, and so, I definitely encourage checking that out as well. And interestingly, even if tooling changes over the years, or you're using Enzyme or Cypress for component testing, the philosophy of testing we're going to talk about today applies in many other frameworks as well. React Testing Library is specifically what we're going to be using.

I see Kampa asked what is the channel for questions? Let me look into that in Discord here. There is an RDB workshops channel, and in there, there's a Designing Effective Tests thread. And so, that would be the Discord thread that you could ask questions on. And if you're having trouble finding it, you can ask here in the Zoom chat. And someone from the conference can help track that down for you. Or you can all help each other, which is greatly appreciated.

All right. So, React Testing Library. Many of you are probably familiar. The npm package for it is at testinglibrary slash react. The website is testing-library.com slash react. And testing library is a suite of testing tools. There's some for just the web in general. There's some for many other different frameworks, Angular, Svelte, things like that. But React Testing Library as React has a high level of adoption, React Testing Library does as well.

4. Testing the Contract and Rendered UI

Short description:

Let's talk about what to test. React hooks have actually helped to sort of prevent testing implementation details. Testing the contract leads to the lowest effort and the highest value tests. Inputs are props and user interaction events. Outputs can be rendered UI and also car calls to external functions. We're going to start with rendered UI. We have a hello world component. Let's write a test to ensure that the hello world message is shown.

So, let's talk about what to test. I've got a component. I want to test it. I or management thinks that testing it would be a good idea. And so, what can I test? Years ago, when I got started in React, a lot of the questions were around, okay, like I've got methods on a class component. Can I call those methods? Or, I've got functions that run when event handlers. Can I call those functions? There's state. I want to see, this code changes the state of the components. So, should I check the state values to make sure the state value changed? These were common practice in React component testing years ago. Now, React hooks have actually helped to sort of prevent that because they were designed to not allow those kind of things and for good reason. React testing library helps with a different approach of component testing as well. Dan Abramov, a very well known React Core team member, had a great tweet in 2019 that I thought was very helpful, so I've hung on to it. He said, we don't encourage reading implementation details like state variables themselves. Instead, test observable behavior, what your component renders or does. This is a great summary of the approach of testing that we're going to spend the three hours talking about. I found another person, another author that's talked about this in another way that helps us wrap our minds around it, I think, he used the term testing the contract. Maybe there's others, but I ran across this from Ed Yarbrough from the book Testing Vue.js Applications. So, this is interesting. In Vue.js, another front end framework, he described this approach to testing that totally applies to React and I would imagine every front end framework as well. In this book, Ed says a component contract is the agreement between a component and the rest of the application. He says further, other components can assume the component will fulfill its contractual agreement and produce the agreed output if it's provided the correct input. Things happen inside the component. Maybe there's state, maybe you're using one of the one million React state management libraries, who knows? Maybe it's a class component, maybe it's a functional component with hooks. We don't know but what the test and the rest of the app cares about is that component has some inputs and outputs. If I give it the right inputs, will the right outputs come out? If we build a test suite where each of our components or many of our components are tested with testing the contract, there's a lot of benefits from that. It gets us the most important things. It also frees us up so that we can refactor the component. We can make changes to the internal implementation details and the contract remains the same. So I've found that this idea of testing the contract leads to the lowest effort and the highest value tests because much more often the tests will support you in making changes and the test won't just be extra work to write whenever you've already changed the production code as well. So let's dig into what it looks like to test the component test the contract of a component. First of all I'll throw the question out and I'll I'm going to make this one interactive if you all would like to answer in the chat in zoom or in discord. When you think about component inputs and outputs what are some of the kinds of input and outputs that components have. So please share your answers. Any answers are welcome in the chat. Take a minute to do that. What are some of the kinds of inputs and outputs that components have. Nan says input props. Ludwig does as well. Absolutely. That's one of the most visible inputs to a component. Other components Arohi I'm sorry I probably mispronouncing yes that's a great point. Output, Kampa says output I think that interpreting that to mean output is virtual dom. Isaker says output JSX nodes. Yeah I would agree. Jan says refs, context, output react node. This is all really great. That totally makes sense. I'm checking over in discord here. Inputs props, output virtual dom and events. Yes that's a great point. We're going to get to events as a key part of what we're doing. These are all great insights. I would totally agree with these. So let's take a look at the way that I've sort of sliced it and organized it for our walking through the workshop today. In testing the contract I would summarize it and this is maybe not I'm not processing live, maybe there's things that you all have said that I've missed here, but this is just the way that I slice some of the main inputs and outputs of components. Inputs are props and user interaction events so when somebody clicks or types that is input. You know when they're typing in text kind of clearly that's input, but even just a click or a scroll is input. Outputs can be rendered UI and also car calls to external functions, that one can get controversial when it comes to implementation details so we'll get into that and talk about that together. So this list of inputs and outputs is going to form the structure of what we work on together and let's go ahead and get started. Rendered UI is what we're going to start with, even though it's not at the top of the list here but in terms of examples this is going to be the most obvious. Output, rendered UI and so let's go to the code. Now we're going to switch to a portion where I'm going to do some live coding. One important note, this is a different code repository than the exercises repo. When you all do some exercises, there's some similar code in there, but it's not the same, so just know that you know when you see something on the screen here. You won't see it exactly the same in the code that you pull down locally. All right, so we're starting with just the very basics with a hello world component. It does have some images as well, an image and an SVG. I have to show this photo, because it's just great, it'll just cheer you up. I found this in stock photography, like look at that squirrel, I can't zoom in very much but that's a happy greeting squirrel so hello, welcome. All right, we have a hello world component. I don't know if I would necessarily write a component test for this, because it's so straightforward, this serves for a great illustrative purpose of if we wanted to test this component or a component with similar contents, how would we do it? So let's jump in together. First of all, about all there is, let's ignore the image and the SVG for a second and let's talk about the text. What we can assert for that is that the text, the hello world message, is shown. So let's go ahead and write a test to do that. So in our spec file, we are using Jest as a test runner. Very, very common in the React world, it works great for my purposes, so I feel no need to look into anything else. And Jest and React Testing Library work together. Jest is the test runner. React Testing Library provides functions for rendering and checking on things and interacting. So, in my test, I like to have one describe block for the component that I'm testing. In here, I'm going to add a test to say, It displays the welcome message. I really like the API of Jest, where you use a string to describe a test because it really encourages you not to be overly terse but to write something in English that's describing the expected output for the input of the component. It helps you to really describe the functionality that's needed. So, to start out, there is a render function. It's the one that comes from TestingLibraryReact, so we get that. And then we just type in the JSX, just like we would type in in our production code to render out the hello component. So, we save, the test runs, and it passes. So, what happened so far is we've just rendered the component, and we've confirmed that it doesn't blow up. The component does exist. It is a valid component.

5. Using the Screen API for Text Assertions

Short description:

The Screen API in React Testing Library allows us to access the rendered elements and perform assertions on them. Using the get-by-text function, we can find elements containing specific text. We can then use expectations like to be visible to confirm their existence and visibility. It's important to ensure that tests fail when expected behavior is not met. By intentionally breaking the code, we can verify that the test fails as expected. The failure message provides useful information for debugging and improving the test.

It doesn't throw exceptions or anything like that. But what else can we do? We can assert on the text that's shown. And the way we can do this for the last, I guess, couple years now is the Screen API. So, we can say screen, which comes from React testing library as well. This just gives us these functions that allow us to access what's rendered out. And in this case, we're going to do—notice that my language server has given us a list of many, many, many functions that are available on screen. We're going to do get-by-text to start out. And we're going to say, hello world. So, this is going to find the element that contains the text, hello world, if it's there. And we'll talk more about different versions of these functions and how they act differently. Once we've gotten this element, we can do an expectation on it. And if you've ever done any jest, you can wrap something in expect to do a check in assertion and expectation. And what we can do here, we'll do to be visible. So, this confirms not only that this element exists, but it's actually visible to the user as well. It's not display none. It's not opacity zero. I think it might even check if it's within the browser window as well. Maybe not. I think it checks that the size is not zero as well. So, let's save and see. The test passes. So, something that you'll see, and this will be a theme as we go through here, it's a good idea in testing when something passes from the start, it's a good idea to make sure that it's not a false positive. Make sure that the test actually fails if what you're asserting for is not working. And because it's easy to end up with tests that just always pass, especially when we get into things that are asynchronous. It's very easy to make a mistake and find out, Oh, yeah, my test says that everything was working, but actually it's broken. So, it's a good idea to go and break your production code to make sure it fails. We can change the text here to make sure that the test fails when that's not found. It does, and so now we can scroll up, it says, unable to find an element with the text Hello world. There's some helpful notes there. It actually outputs all of the DOM, which is helpful when it's a small component. It's maybe a little less helpful when it's very large, but this does help you see what's going on. And we can see that the greeting message is very is non-sense here. It also shows you the line of the test that the failure happens on. So, now we can go back and fix it and make sure the test passes. Another good reason to see the test fail is to see the failure message. Sometimes you can adjust the information that's given to it to make the failure message a bit more helpful for you or somebody else in the future if the functionality breaks.

6. Testing Environment and Accessibility

Short description:

Jan mentions that the Node scripts start build and tests don't work on Windows due to how environment variables are set. React Testing Library focuses on testing the app the way a user interacts with it. It is important to provide text-related descriptions for images and SVGs to ensure accessibility and assistive technologies. Coupling tests to shown text provides realism benefits, but can lead to test failures if the text changes. The React Testing Library community recommends updating tests when changing text is not costly. Additionally, the get-by-selector function is not available for third-party components, which can complicate testing. SVGs can be imported as React components and tested for their presence using the title prop.

Jan says, The Node scripts start build and tests don't work on Windows because of how they set environment variables. Yeah, crossenv. Cool, Jan, thanks for that. Yeah, I am unfortunately do not give as much attention to Windows as I should. So, thank you for posting that. And maybe that's helpful to other folks as well. Maybe during the first exercise, I'll push that change up. Jan, if you would like to make a pull request, if you have time, against that exercise repo that could help me out. But I'll try to get that change in during the first exercise as well. Alright, so let's continue on.

So, by default, the Get-By text does not match substrings. So, if I just want to check that, Hello is shown when I save, it says unable to find it. We can get around that if we do want to match a substring by using regular expressions. You may have run across those in JavaScript before, but putting it in slashes means in the string that I'm in search for the text hello. And there's all kinds of many, many, many features of regular expressions that let you search for things more sophisticated. But if we make this a regular expression, this says, yes, I found a text that matches this matching pattern, this regular expression. So, now, for matching things that aren't text, so in our apps, we have a lot of visuals as well, images, SVGs and things like that. So, this gets us something really interesting and helpful about React Testing Library. React Testing Library is focused on testing the app the way a user interacts with it, and it turns out there's some helpful accessibility practices for there. When we're writing in code, we can't very easily get something by, maybe if we get AI in here, it can say, make sure there's an image that looks like a squirrel and happy. We can match that. We don't have that currently, maybe next week. For now, we need to write text in our test to match on things. And so, this means we need something text-related on the image that we're going to be able to match against. And so, there are abilities to do that for accessibility, for images, for SVGs, you can provide a text description of those. And that doesn't only help your test. It also helps people who are blind or people who have limited visibility and are using screen readers technology to be able to interact with your app. So, there's this really beneficial thing that comes out of this, where by writing a test, you're now using your app in a second way, other than just interact with it directly. And if you're somebody who doesn't have visual impairments, your tests don't see the code that helps to ensure that your app is accessible. If you write your test the way React Testing Library recommends. In this case, I have a linter that's failing already. JSXA11Y is saying, image must have an alt prop, meaningful text or things like that. If we fix this, this is going to give us something to assert against. So, we can say alt squirrel waving, that's the description of the image. And now we can query against this alt text in our test here. So, we can say it displays the squirrel photo. We can copy and paste the render. And we can say expect screen get by alt text, squirrel waving to be visible. Save and it passes. Again, don't trust it. Let's see it fail to make sure I've got it right. If I change the alt text, it says it failed. It could find an element with alt text squirrel waving. But in our output DOM, we can see the alt text. This brings up an interesting point about tests where when we're searching based on text, this is kind of coupling to text that is shown. This kind of requires your test environment to be set up if you're using an internationalization library, your test environment needs to be able to be set up to be able to output text for a given locale. And this also exposes you to if your copy changes, if the design or UI UX folks come in and say, oh, we want to rephrase how that's done, your test might fail. So there's a tradeoff there. What the React testing library community has found in their view is that you're better off coupling to text that is shown because of the realism benefits you get. And so, the downside, they would recommend that when you're writing these component tests that are consensual with the components. If you're changing the text it's not a lot of work or expense or complexity or cost to update the test to match that text. And you get enough of a benefit for realism to be able to make that worth it.

This is a great question, is there a way to increase the max size of HTML output if a test fails? My components are too large oftentimes. I have that question as well. I'm looking forward to. You can use screen debug, pass a maximum output size. That's helpful. I learned something today. Thank you for sharing that knowledge. That is going to help me on my big Material UI data grid component I'm working on right now. Great tip. Everyone, please feel free to continue to ask questions and share tips with one another as we go along. Let's see, one note about accessibility. I do wish the React testing library had the get-by-selector function to deal with third components. Yes, that's a great point. When you're working with third-party components where you don't control the elements underneath it, things can get complex in a lot of ways for testing purposes.

Alright, let's talk about the SVG as well. SVGs can be loaded in in different ways. I'm using create React app here. And in create React app by default, you can import an SVG file as a React component. You can give it a name and render it out like this. In this case, your SVG setup might be different, you may need to look at something else. But in this case, SVGs have a title prop, and we can add a title prop to test for the presence of this waving hand SVG. We can't visualize it. Can you visualize it? This time, let's write the test first. This is test-driven development in a sense. You can write the test and watch it fail before you get it passing. Let's just try that to see what that's like. We can say it displays a waving hand icon. You can render out the hello component. Then we can say expect screen. In this case, there's a get by title. There are so many different functions as a part of React Testing Library. Some of you might be familiar with other ways that you can query these things. Those are very welcome as well, there's not just one way to do it. Clearly, when you look at the many many many different functions. But I found that get by title works. So, we can say get by title waving hand to be visible. I want a waving hand icon to be visible. When I save this now, the test fails.

7. Benefits of Test Driven Development

Short description:

Writing tests before implementing functionality in test driven development can be helpful as it ensures that the test is correctly catching the desired functionality. While it can be more work to write the test passing after breaking the production code, it can be easier to first see the test fail. Test driven development is not the main focus of this workshop, but it can be useful in certain situations.

And that's as we expect, because we've written a test before we've gotten the functionality working. Unable to find an element with the title waving hand when we see an SVG there with no title added. Now, to get this to pass, we can add a title prop to the SVG component. Waving hand. Let's save and the test passes. So, this shows one of the reasons that test driven development can be helpful. If you're taking the approach of, I want to make sure I see my test fail. Because I want to make sure it's correctly catching my functionality. It's actually more work to write the test passing, break the production code, and then write the test ‑‑ you know, fix the production code to get the test passing again. It can be easier to first see it fail because you wrote the test first before you got the component working. I'm not dogmatic about test driven development. I use it when it's helpful and useful. It can be challenging with UI work like React work. It's something you can reach for sometimes. But that's not the main focus of our time today. I just wanted to take the opportunity to show that.

8. React Testing Library: Getters and SVG Testing

Short description:

In React testing library, there are getter functions on screen like get by text, get by title, and get by label text. These functions allow you to access specific elements based on their text content or labels. Alt text is important for images, and you can use JSX11y to ensure it is present. There are different ways to access SVG elements, such as using the title attribute or exploring other options. Feel free to experiment with SVG testing during the exercise time.

All right. With that, we've done some basic testing of rendering to move on from there. Hold up the slides.

All right. So, let's talk a bit higher level about what we saw. In React testing library, there are getter functions on screen. Get by text, get by title, get by label text, which is useful for forms which we're going to see a little bit later. So, just to show you those, to summarize them, if you want to get text, you get by text and there it works. If you have any text rendered out, regardless of the element contained by, get by text will return it for you. Here's an example of the form element just to give you a preview of what's coming later. If do you get by label text, you can enter the label that is shown and it will give you the input rather than the label. Here I show the example of where in HTML I'm wrapping the input inside of the label that's containing it. But this works just as well if you use the HTML4 and the id attributes to have separate labels and inputs connected to each other. Again, it's this react testing library philosophy of testing like a user. A user knows what input to type in because they see or have a screen reader tell them that enter your name is the label for it. Our test are accessing the input in the same way instead of a test ID or some lower level code thing. Alt text. Images have alt text. Hopefully you have JSX11y that's warning you if you're missing alt text for an image. If you have that present there, you can pull it up. Vicky says adding title on SVG does not make sense. I think. Cool. Thank you for that link. I'll check that out. If we can find another way that allows SVGs to be pulled up in the test and that's great. Ideally, it's an attribute that's useful both for production use by users as well as by a test. So, I'll check out that link that you shared and others feel free to add recommendations in here as well. SVG title. Again, summarizing this, I found that this works. The title is a document attribute and there is a getter to be able to pull that up. But yeah, if that article shared might be another way to pull it up. The key is that with the SVG technology you're using that the attribute you add is in fact outputted on the SVG. And maybe ARIA label is. Not sure. This could be something you can feel free to use the exercise time to play around with this if you would like and see what works in this toolset.

9. Choosing the Right Query Method

Short description:

In React Testing Library, the recommended query method to use is get by role. It is the top priority in their list, followed by get by text, display value, placeholder text, and label text. Test IDs, although an option, are not included in the recommended queries due to their limited visibility and accessibility. While get by role is highly recommended, it requires learning about roles, which can be time-consuming. For a basic introduction to React testing library, it is suggested to explore get by role and its benefits.

So, which query should I use? We've seen so many different getters and so is the question is like which should you choose? There's test IDs, that's an option, should you use that? First of all and foremost in React Testing Library they describe a philosophy of how to think about queries and how to think about your tests. And they say your tests should resemble how users interact with your code component of page as much as possible. That's what I've been kind of trying to describe as we go along. And so this leads to a certain priority order. In their list of priority order they put get by role at the very top. We'll talk about what that is in just a second. Then after that, get by text, display value, placeholder text and label text. And they describe like the trade-offs, the pros and cons, but they try to organize them in a priority order. If you've used test IDs before notice that that's not even included on this slide and it's way down at the bottom on their list. A test ID is something specific you put in your code for testing. There's benefits to it. But the downside is just because your test can get something with a test ID, the user can't see that. So, the user might not be able to find what they're looking for. It may not be accessible. There may not be a label or the correct accessibility role. So, preferring these queries recommended by react testing library can help your app to be beneficial in other ways. So, a note on get by role. It is the top recommendation for the query method to use. And we're going to be using it. I'm going to show an example of using get by role in the forum coming up a little bit later. I do recommend using it. However, there is learning needed. It's important learning. You should do that learning. I highly recommend it. It requires learning about roles. The project I'm on now, we're skewing for get by role. I'm learning about column header, learning menu items. We're using the material UI library. It's great. Very helpful. But there's a learning involved with that. And so, for the sake of just basic introductions to React testing library, I wanted to not always use get by role because in three hours, it's hard to teach accessibility at the same time. I'm in the process of learning as well. I'm not the best accessibility person at all. Very much in process. So, my recommendation is check out get by role. You'll see the benefits of using it. It improves test readability as well.

10. Confirming Presence of Elements

Short description:

Confirming the presence of elements is crucial in testing. The recommended approach is to use expect screen.getby to be visible, which checks not only for existence but also for visibility. However, in some cases, you may want to confirm the presence of an element even if it's not visible, such as when it's display none. In such cases, you can use expect screen.getby to be in the document. Vicky suggests using unique attribute values like ID for finding elements, especially for SVG accessibility.

Confirming presence. So, now that we've gotten our elements, we have to you know, confirming they're present is one of the most common things you want to do. Is it there on the page? Really, the way I would recommend, and I think is recommended to do that is expect screen.getby whatever to be visible. So, this makes sure that it exists that it's there, but it also does additional visibility checks like to make sure it's not display none, to make sure it's not opacity zero or height and width zero. And this again, fits with that philosophy of testing like a user. If something is present, but if it's display none to the user, it might as well not be present. That's an implementation detail. And so, expect to be visible probably most closely aligns with what you're thinking about in terms of what your user needs. There are cases where that's not the case, though, where you want to confirm like it is display none, but you just want to confirm that it's there, that it's present. If that's the case, you can use expect screengetby to be in the document. And that does some extra check-in as well to make sure that it's not a DOM node that's still present, but got removed. That can be an issue in testing in general, to make sure it is a DOM node that's present in the document. Vicky says in the chat, if we just need any unique attribute value to find the element, I guess it could be ID of the title or area labeled by if you add those for SVG accessibility. Cool. That looks like a great CSS tricks is a great resource. I'll check out that link. I'll add this to the workshop webpage as well for reference.

11. Confirming Absence with Query By

Short description:

To confirm absence, you can use the query by function in React Testing Library. Unlike the get by function, if an element is not found, the test will not fail immediately. Instead, the query by function will return null, allowing the test to continue. To assert that an element is not present, you can use expect screen.query by not to be in the document. This provides a more expressive check than simply checking for null. While using query all the time would work, the get by function provides more helpful error messages when an element is not found. It communicates the intent of the test and helps future developers understand what is being confirmed. By setting up tests with helpful failure messages, you can aid in debugging and improve the overall quality of your tests.

Alright. So, confirming absence. So, now, there's the opposite side. You want to make sure something is not present on the page. How can you do that? It's similar, but a bit of a difference needed. In addition to getter functions, there's an analogous query functions as well. For every get by, there's a query by, as well. The difference with query is, if something is not let me back up, with getters, with get by, if something is not found, the test immediately blows up. We saw the errors we saw where it says, this was not found. We saw the DOM or the JSX tree outputted there. It's a very helpful error message. There's cases where we don't expect something to be found. We need to confirm something is not there. Query by is useful in this case. Query by will return the element if it's found. If not found, it's null. Your test is able to continue. The assertion you'll tend to want to do is expect screen.query by not to be in the document. You checked if it is null. You can check if it returns null. But not to be in the document is a bit more expressive. That's what matters. I don't care if it changes to undefined in the future. I don't care if it is null or undefined. I care that it is not an element in the document. There is other checks to be in the document as well. This is something you need to know if you are copying and pasting code to confirm something is present and absent. Ideally say get by to be visible and expect query by not to be in the edit. There is a bit of verbosity there but using those APIs gives you the optimal assurance. Alexi asked one of the questions that has been my biggest question for years. Is there no reason to wrap get by and expect since they throw anyways? To be visible does more checks. So, yeah. This before to be visible and in React Native there is a similar question. Get by will already blow up so should I wrap it in an expect? Kent has great resources on this and let's see if I can briefly summarize. In addition to the to be visible check, which is other checks, it is helpful in your test to wrap it with expect so you can see I am intending to use this as a check. If you just had the get by by itself, another developer, maybe you in the future might look at that and say is that leftover code? Is that accidentally copied and pasted? It doesn't look like they're intending to confirm that something's the case. But when you wrap it with expect, it communicates to the reader of the test, I'm making an assertion, I'm confirming something here. That's the intent of what I'm doing. It did feel kind of weird, like in React Native, I wrap something in expect but it's not adding any more functionality. But it is valuable to think about what your tests communicate so they're helping the reader understand what it is you're trying to test. But in the case of the web, we have the ToBeVisibleMatcher which provides extra helpful checks. I'll find the Ken C. Dodd's article and include it on the workshop page. I'll share it in the chat if I find it during our first break.

So, that's the first part of testing the contract. We've covered rendering UI. We're going to move straight on to props because that's very similar. It doesn't take a lot more work to get into props, so let's do that. So, let's go to the code. And after we do props very briefly here, we'll have our first coding exercise. So, let's change our component. Let's refactor it to take in a name that's customizable. So, we're going to have a name prop here. We're going to pass in world by default so that it keeps working even if we don't pass a name prop. And then we're just going to output HelloName down here. Let's Save Changes. Notice that our tests all still pass. So, our test that checks and welcome message, even though the name can be configurable when we don't pass it, the test still passes. Although, I'm not checking for the name here. Let's go back and Hello, world. That still passes as well. So, we've been able to refactor and change some of the internals of our component. We haven't broken something that was working previously. Asegar says, is there a point to using get at all? If query plus expect fails, anyway, something is not found. Throwing feels like overkill. That's also a question I've asked. That's a great question. Your test would work if you used query all the time. For a while, I did do that. Query for when it's there and when it's not there. You do get a bit more expressive errors in the case of get by, because when you're using query, you just get a null back. And so your tests tend to, actually, let's show it. Let's show it. Let's say, get by text. And we have a failure. So, here we say, I'm able to find an element with the text, hello blah, blah, blah. And we get the don output. So, let's try changing that to query by. So, now, the output we get is receive must be an HTML element, but it has no value. First of all, the error output is not as readable. You can see the line of code there, but your brain has to parse this. And you don't automatically get the output of the DOM that was present. And the reason why is, kind of the way that JavaScript is executed. When React Testing Library is running this, it doesn't know if you want, it just knows that you're trying to query by this and it should return null if something's needed. When Jest runs and it says expect null to be visible, it doesn't know you ran a query by, it just says you have a null here and that's what it outputs. Maybe folks that know the internals of the libraries can make query by fail in just the same way. But having to get by fail early, you're getting, currently, with the way code's currently implemented, libraries are implemented, you're getting this more helpful error output. You will still get the right failures and successes using query by. But think about that philosophy that I said of help yourself and help future developers and future you by setting up your tests to have helpful failure messages. That's why React Testing Library recommends using get by for positive finds.

12. Testing the Movie Row Component

Short description:

To thoroughly cover components with tests, it's important to test both sides of conditional branches. React Testing Library allows JSX syntax for rendering, making tests more readable. The exercise focuses on testing the movie row component in isolation from the rest of the application. The goal is to write a test that covers all functionality, inputs, and outputs of the row. The exercise repository contains the necessary files and instructions. The movie row test example demonstrates testing the display of the movie name using a minimal movie object.

Oh yeah, and Jan says if you want to click an element, you have to do a null check with query by, with get by you can use it directly in fire event. Cool, that's that's a great point as well and we'll see that when we get to clicking shortly. Let's fix this. So back to where we were, we refactored our component to add a prop and we saw that our existing test continued to pass, but we do want to add a test for this new functionality as well and we do have a passed in name, so let's add that in. It renders the pass in name. This is important when it comes to thoroughly covering components by tests, when you have a conditional, even a conditional of supplied value versus default, it's important to test both sides of that to get full coverage so you know that both sides work. If we didn't have this test, we wouldn't know if the component might blow up if we didn't pass in a name prompt. I mean you need to make the choice in your own code whether you want to get full test coverage of all branches. There's a lot of pros and cons there. There's a benefit to getting more coverage in a case like this. Let's say, hello name Josh. So what do we expect? We expect that what we're going to see is, hello Josh to be visible. Save and it passes. And again, let's not trust it. Let's see it broken. This is a trivial case, but I've been burned enough by false positives that I kind of almost instinctively like let's see it fail. So it fails when I'm not outputting the name. When I output the name, the test passes. So we're covering that prompt. And I'm sure there's more cases that we can get into, but that's basically what I have to say about props in your tests. So let's look at that. Yeah, the end. You can pass in a prop. One of the nice things about React testing library is that the JSX syntax for rendering is exactly like the syntax you use in your production code. It really helps with test readability. That's not the case for all front-end libraries, but it works out well for React. So with that, we have our first exercise. And again, we're going to kind of combine the break with that as well. So React testing exercises. Oh, I might have gotten the name of that repo wrong. Let me check that out to see to make sure. I'm going to pull up workshops. The exercise repo. This is the workshop web page. Oh, yeah, that's right, React testing exercises. No, that's not right. That's not right. Sorry, everyone. I think, what have I done? Too many workshops. RTL exercises. Okay. I think the other one might work as well because they're very similar. But let me put this in. Sorry about that. Jan probably did the pull pull request to the other one. This is the correct exercise. Sorry about that. I'm gonna get that updated on the web page. RTL exercises for designing effective tests. Let me show you what you'll see when you pull that down. All right. Let me pull this up over here. Again, as a reminder, this is a different repository from the sandbox repository that I was demoing out of. This is just focused on what you need for your exercises. There's a read me here. Windows will need that addition that Jan said. As it's described in here, there's an exercises folder. Right now we're doing exercise one. This is kind of a cohesive app where we're managing a list of movies. And we have a movie list, we have a movie row within the list, and a new movie form. You should be able to get this running locally and kind of see it working. But we're testing one component at a time as we go through. And to try out testing rendering, we're going to test movie. I think I'm on my solution branch here already. So movie rows, very simple. I've tried to keep it as simple as possible to not distract with, you know, third party UI libraries or anything like that. But this is a row where we output the title of a movie. And if the server gives us a boolean property added today, if the movie was added today or if it was not, true or false, based on whether it was added today, if it's added today, we use this new SVG icon to show. I have a title prop there, which maybe there's better ways to do that. So the goal of the exercise is let's write a test for movie row that covers all of the functionality, all of the input and outputs for this row in isolation from the rest of the application. So that's really the goal. There's a movie row spec file. I want to open this up because this has my solution on there. Um, but yeah, um, there's some tips on here on how to design your tests. Um, there's some, uh, notes on here, a link to the testing library docs with information about, um, those APIs that we've seen together. All right. I want to show, um, the movie row test that I wrote. Um, and kind of explain my reasoning. And then, uh, any input from y'all are what is welcome. Um, certainly if you found other ways to approach the SVG here. So as I was thinking about the movie, row component, and let me, uh, Let me run this test new tab with current profile. There we go. Gotcha. So for the movie rope, um, I wanted to test that the name was being displayed. So I set up a movie object here. Um, note that actually in this test, I only provided one property on the movie. Um, the others were not required, like the test didn't blow up when that wasn't there. Um, I think coming back from the server, there's IDs might be a created ad and things like that. Um, but my tests only require this minimal data. And so that's what I put in.

13. TypeScript and Test Code

Short description:

If using TypeScript, it may be more strict depending on the setup. One suggestion is to write test code in JavaScript, even if the application code is in TypeScript, to be more flexible. This allows for simpler test data and avoids the need for complex object structures.

Um, if we were using TypeScript, it may be more strict, depending on how you have it set up. Um, but I, one of the things I haven't used TypeScript a whole lot, um, but one of my friends who uses TypeScript, he actually recommended, even when your application code is in TypeScript, writing your test code in JavaScript, because it allows you to be a bit looser, like in production, you want to make sure that all the types match up, but in a test, it might feel like jumping through hoops to provide a full object. And literally with one simple value can help. So take that suggestion for how you like. I'm sure there's a lot of folks that write their tests in TypeScript as well.

14. Testing Rendering and Props

Short description:

I separated out into three different tests. There's pros and cons of that. But one of the big pros is test readability. Test ID is discouraged in React Testing Library. Storybook is a tool you're likely familiar with. Rather than trying to shoehorn that into React testing library, I find it better to use a tool like Storybook. What about snapshot tests? Downsides to snapshots, the fact that it's not expressing the user's intent. I have found the behavioral testing that we're doing here in this workshop to have higher value for me and using something like Storybook to be able to easily visualize so that you and your designer and product owner can see what things look like and use your human brain to check on the visuals to make sure that something doesn't look weird, or off, or not optimal.

But the idea of minimal test data to keep the tests nice and readable worked. So I got by text the movie title, and I checked if it was visible. Then I added two different tests for rendering the icon when the movie was added today, and not rendering the icon when the movie was not added today. Yeah, so I check, get by title added today to be visible, and then when it's not there, when added today is false, I check that, I do query by added today and make sure that it's not in the document. And again, these are the tests that if we find a different way to test for SVGs, we could totally do that.

So another interesting thing, and this was a judgment call that I made, I really like, and you'll see this as we go through, to have small focus tests. Sometimes folks say one assertion per test, or you could say one expectation per test, in Jest. And that's what I'm doing here. Sometimes that's a little bit far, though. Say you had a form with a first name and last name, would you have two different tests to make sure the first name and the last name were shown? It's kind of the same thing, and so in that case, one way I've heard it is to test one behavior per test. And so in this case, you know, rendering out the data is kind of what happens here, but I had it a conditional test. I needed to test when the icon was present and when the icon was absent. I could have put the title in both and checked for the title twice. That felt a little duplicative. I could have picked one and said, oh, I'll check for the title as well as checking for the new icon as well at the same time to get down to two tests instead of three. But really, I kind of liked the way it worked out here. The fact that it's kind of different situations. This is kind of what's shown unconditionally, and so if there was a movie director, I might put that in the same tests here as well. But then there's these two different conditions, there's two different situations you're in, whereas the first test doesn't care about whether it's added today or not. So, in this case, I separated out into three different tests. There's pros and cons of that. But one of the big pros is test readability, of being able to see. I think the jest approach of having these descriptive English test names guides you to this in terms of I want to be able in one sentence what the test is about. It's hard to say when it's added today, display the name and display the icon but actually you always display the name. Summarizing it feels awkward. I let it easy to summarize behaviors of the component and contract that are easy to summarize and let that guide test structure I make. It doesn't hurt things if you group things in a different way.

While I'm getting ready, I do want to keep us moving on, but while I'm getting ready for the next portion of our demos, yeah, any thoughts or feedback you all have different approaches you took to your own test for this exercise or things that you would suggest differently, feel free to put that in the chat. Lottie says you can also add data test ID to the SVG and use get by test ID. So yes, you totally can. I guess I should say I haven't directly tested that on SVG but I trust your assertion. Test ID is discouraged in React Testing Library in general, and so I'm not sure, if title is bad to add to an SVG, it's probably better to use test ID than title, but if title has any benefits, maybe it's better to use that. Vicky says, Why not to use data test ID? Cool. Yeah. Alexi says, Maybe, it makes sense to test the absence of SVG badge and we don't pass the AddToday property at all. Totally. That's something that I think about a lot in my test. Are there cases, do I care here about truthy versus falsely versus null versus undefined and actually in JavaScript objects, and undefined value is different from the key is not present at all. So depending on where your code is, you might care about those differences, or you might not care about them. And it might make sense to cover all of them individually in your case, might make sense not to. These are the judgment calls that come into when you're writing to your test and deciding how much detail you want to get into.

Let's pull up the next slides. A few more notes about testing rendering and props. Testing appearance. Storybook is a tool you're likely familiar with. It's been around for a number of years now. And it allows you to see a catalog of components in your app, and you can very easily specify different arrangements of props. I saw a great talk by Michael Chan at React London. When it comes to testing visuals, we've talked about testing the contract of a component and testing its behavior and we've focused on things that are testable. So that doesn't test the look and feel. Check out this graph. How would you write a React testing library test to make sure that the gradients is right? You can test the circles are the right size, but that they're the right color, that it looks good. That this text isn't too large or too small. Rather than trying to shoehorn that into React testing library, I find it better to use a tool like Storybook. If you want to visualize the different stages of that component, bring it up visually.

What about snapshot tests? Jest snapshots of JS, this syntax may be out of date, but at the time, this is how you would do it. You have some JSX, you use a React test render to render it out. You can convert it to JSON, and then you say you confirm that matches the snapshot. And something for folks that are new to React might not be familiar with, the word snapshot doesn't mean it's an image of the visuals. It means taking this JSON here, it's a snapshot of that data structure. So you're saying, does this component render out the same data structure of JSX, JSONified, that it did before? And it automatically records the output and updates it later. It's not a visual, that's testing the output. Ken C Dodds has a blog post where he talks about just snapshots. He actually responds to something by Justin Searles, who is a co-founder of Test Where I work. He talks about some downsides to snapshots. I don't think I put this blog post on the workshop page, so I'll add that in because it's a helpful one. Downsides to snapshots, the fact that it's not expressing the user's intent. How do I intend this component to render out? It's not clear here, and it's very easy and very tempting for snapshots to just say, when it says, oh, something has changed in the snapshot, just to say, yes, it's fine. I ran into a case in a project where I was doing a refactoring and the component looked and worked exactly the same, but the snapshot changed, because the internal data structure of the JSX had changed. I think it was some key values or something like that. That I don't find valuable. That's not a useful test that's adding a lot of help. It's work when it fails. I do use snapshots for something else. I have a command line tool that generates a README file, and I use snapshots there to easily capture what the README file looks like. For React components, I generally, and what Ken C. Dodd says is, in that blog post, don't do a snapshot of the whole component, do a narrow snapshot of just a portion of it. And in fact, if you narrow down all the way, what you're going to end up doing is not using snapshots at all. You're going to say, look for this text, look for this SVG, or look for something else. And that's the advice that I would follow. If you find that snapshots work for you, great. Go for it. But I have found the behavioral testing that we're doing here in this workshop to have higher value for me and using something like Storybook to be able to easily visualize so that you and your designer and product owner can see what things look like and use your human brain to check on the visuals to make sure that something doesn't look weird, or off, or not optimal. Cool. I see a lot of notes in here. So, just to stay on track, I'll let you all chat in the in the chat here, because I want to make sure we get through everything in our three hours and I'll, during our breaks, I'll try to catch up on the chat and interacting there. So, number three actions and mocks. This is our second segment testing the contract. So, next we're going to talk about this other type of event input user interaction events, which is a type of input. So, let's go to the code.

15. New Message Form

Short description:

This is a simple form for a messaging app. Users can type a message, click the send button, and the on send function is called with the input text. After sending, the input text is cleared.

So, new message form. So, this is just a very simple, just the most trivial form you could imagine probably in react. It's like for messaging app. You type in a message and you send it. You have an input. Got some nice label around it for accessibility. We have a send button, and when we send it, there's an on send prop passed in here. It's just a function. Maybe it hooks you up to Redux, maybe it hooks you up to a larger application, React query, who knows. We have a prop for on send passed in, or if that's present, when we submit the form, that's called and the input text is sent to it, and then after that, we clear out the input text as well, because the user, once they send one message, they don't want to have to send it repeatedly.

16. Testing the New Message Form

Short description:

To test the form, we focus on the behaviors of clearing the input text and calling the on send function prop. We use the user event library to interact with the code and render the component. By typing in the input field and clicking the send button, we confirm that the text is cleared and the on send function is called. We use specific queries like get by label text and get by role to ensure the correct elements are found. The test passes, confirming the expected behavior. We also discuss the importance of using get queries over query queries for better error messages and stability. Finally, we address the question of testing implementation details and conclude that as long as we check something visible to the user, such as the value prop, it is a valid test.

Let me get my notes pulled up. There we go. So how can we test this form? Well, what are the behaviors? A way I would summarize it is, when we click send, that's the situation we care about, after we type in and click send, we fill out the form. There's two things that happen. We clear out the input text and call the on send function prop. So let's test one of those at a time. It's helpful for the workshop, but also I like testing one behavior at a time because I like separating out the tests in that way.

First, let's do clearing the input text. So in our new message form test, I like to add a describe here, say when the form is filled out. Because you might also test the initial state before the form is filled out. So we're going to say here, it clears the message field. That's the one behavior, that's an output, it's a change to what shows up in the rendered JSX tree. And so the first thing here, we're using this user event library, which is not newish, but there's a new version that's much better that improves even further on it. But user events in the library for interacting with your code provided as a part of testing library. So as a version 14, the API to use as you get a user object from saying user event dot setup. Then we're going to render our component, new message form, and then we're going to type. So let's start it just by typing. We do need an async test here, because we're going to, oh, wait, await user dot type screen, and this time we're going to get by label text, because we have a text field that has a label. That's very important. That's from the early days of the web, I remember, the importance of labels for accessibility. And the label is message. And what we're going to type in is hello world. This may be a bit of a different syntax that you're used to if you are still using fire event, or if you're using an older version of user event. User events methods were inconsistent in the past as far as whether they were asynchronous. But now they are consistently synchronous, consistently asynchronous. That's great for consistency, and it's also helpful for other reasons we'll get into a bit later. So we're going to type in hello world, and then we're going to click, and that's a wait user dot click screen, and this time I'm going to use get by role, because this is going to be one of the easier roles to learn about. Button is a role, and it's important for buttons to have the role button so that screen readers can communicate to the person with visual impairments that it is a button that's interactable. And we say button name send. So this is nice because we're not just looking for text send, maybe send is a heading of the area as well, but we specifically find a button with a title of send. If someone asks in the chat if there's only one button, you could just get by button, and then you're fine, but this makes it more robust so if a different button is added in the future, we make sure to get the send button, and we also confirm that the user can see send as far as something that's specified on there. So this is going to allow us to type and click send. The test passes, so nothing's blown up. This confirms that these are actually found. Let's go back to something someone said earlier as far as get versus query. Say I say label text message exclamation point, exclamation point. We save and that's not found to type in. And we say unable to find a label with the text of message, and we get the output here. If we did query there, then this is actually even a worse error message here. Can I read properties of null, reading disabled? This is not very expressive at all. Why is there a null? What have I done wrong? It's not very clear. By using get by label text, we tell the React testing library I expect there to be an element there. And it gives us a nice clear error message. Unable to find a label with the text of message. These are helpful when you're going into tests written months and years ago and something has failed and you want to understand why. So readability error message is helpful. It's worth the complexity of having to remember get versus query, I think, lot of times. We need to make an assertion here. This is all enough to do filling out the form. But how do we confirm the message field is cleared? We can say expect screen, get by label text again to get the message input. And we can say to have value empty string. This is to say we're confirming that that text input is empty. It has been cleared out. And it succeeds. By now you should know we don't trust it. Don't trust it when it's green. We need to see it fail. So let's comment on the production code that clears the input text. When we comment it out, we see expected the element to have value. It's an empty string. It's so easy to see in there. If you're used to this, you can see expected to have the empty value. But instead we received hello world. This confirms that the input is cleared out. We save it and it passes. So something this, yeah, I don't think this is in the slides. Let me describe it now. When you see this, depending on your thinking on tests, you might say, well, hang on a second, I'm checking the value of this element. Value is a prop. That's a prop that's passed to the input. Is this testing what the user sees or is this an implementation detail. The user doesn't care that you have an input HTML tag and you pass a certain value to it. The user cares about what the text is that's displayed. So is this testing implementation detail? And what I would say is, I haven't figured out a great way to summarize this, but basically, like we are testing that the text is cleared out. We're testing that the user sees an empty input field. But to have value is the name of the matcher that does that. If you don't like the name, you could make a custom matcher, you could do that in Jest that says to be an empty text field, you could describe that. And that is maybe a bit more expressive. But as long as we're checking something that is visible to the user, if the method we're using is to have value, and that's checking the value prop on there, that's something that's stable. I've been a web developer since maybe 1994, in high school, middle school, and the value attribute of inputs has not changed in that time. So I think it's stable. I think we're going to okay. There's nuance in testing. And there are tradeoffs. And so, the idea of don't test implementation details. That's a good principle to go by. But it requires some thought. Like, what is and what isn't an implementation detail? And we're going to see that a bit later when it comes to mock functions. All right. Let's go on from here.

17. Testing External Functions and Mock Functions

Short description:

To interact with components, we can use the user event library to simulate user input and interaction. User event version 14 provides an improved API that better mimics user behavior, making tests simpler and more reliable. The Jest DOM library offers the toHaveValue function to assert the value in a text field, providing a safer way to check the props of an element. When testing external functions like function props, using Jest mock functions is recommended. Mock functions allow you to check how the function was called and set up return values for testing scenarios. By using matchers, you can confirm that a mock function was called with specific parameters. It is good practice to test for exact values when possible to ensure more robust and accurate tests.

So, to have value is provided by the Jest DOM library, and we'll see more about that in the slides.

Cool. Let's go over to the slides. And so, here is just and I'm going to go into play mode here, so I can see the highlighted fields. This is the version of this test I wrote, maybe the same, maybe there's a typo difference. But just to highlight the lines to see what we saw before. Interacting with the component, you know, user input, user interaction isn't input into the component. And so, we can do this with the user event library by calling user dot type and user dot click. And there's some others we'll see later. So, in user event version 14, this is the API. This is a change from user event 13. So, you call user event dot set up to get an object and then user dot type. There's a user dot clear which will clear out a text field as though a user deleted the text out. There's also select options for when you have a select dropdown and you need to choose an option or multiple options. In case of a multi select. Not too common anymore. A lot of us are not using create React app anymore. If you're using create React app, it installs version 13 by default. But I really, really strongly recommend updating to user event 14 and maybe in your own projects you can pull in user event yourself and you have it updated. Version 14 has a lot of robustness. The fact that everything is asynchronous helps with some of the asynchronous issues in testing. In my book, I had a whole chapter working around, oh, we're getting the act warning in React. How do we get around it? Asynchronous and complex and tradeoffs. But when I updated the book to user event 14, it was simple. Because the user event acts more like a user. They do focus events when you click in a text field. Value change events, focus off, things like that. It's more realistic and it's realistic that users take time to interact with your form. Things just fall out. Your tests end up being simpler and more reliable as a result. I highly recommend updating the user event 14. You'll see a lot of benefits from it. As far as asserting the value in the text field, we pull up the text field by label and call to have value here. To have value is provided by Jesdom. There's other ways to do it to get to get directly to the props of an element but using this function here, you know, by a third party library, it's something you can check. That's a bit safer than manually digging into the details of props. Maybe React or React testing library, internal data structures will change. As long as Jesdom supports to have value, it's going to work reliably. So, that's user interaction events. There's much more. So, let's get our first steps into calls to external functions. There's actually multiple ways to do this that will take us through the rest of our time together. But, just to get this testing working, we need to do some testing of an external function. In our case, there was a function prop. So, we need to see how it was called. Really, when it comes to external functions outside of a component, there's a few different ways, types of external functions that are maybe tested in different ways. There's function props, there's function from hooks, which opens a can of worms. Hooks can of worms. There's functions statically imported from modules. So, you do an import from another module to get a function that you call. We'll see that separately. But right now, we're just gonna take a look at function props because that's what we have in our form that we saw a minute ago, that on-send prop. So, for testing function props, I recommend using Jest mock functions. So, here is the page, these slides are available on the page there, but this will be linked in the exercise readme as well. But if you just go to JestJS.io and look for mock functions, you'll see the docs for it. A mock function is any kind of stand-in for an external function in a test. So, one of things you can do with it are you can check how it was called and you can set up return values that are helpful for testing your scenario. To create a new mock function, you do this. You call Jest.Fn which is nice and concise. It's maybe not the most descriptive, but it's a Jest function. It's not another function. It's a Jest function which is a mock function. I always recommend adding calling this mockName to give it a name and we'll see a bit later in the example code. It helps with test failure readability. What can you do with a mock function? You can call these matchers on it. You can confirm that a mock was called, you can confirm that it was called with specific parameters, and even if you care that it was called with something, you don't know exactly what the value is. Jest has the APIs like expect any string. This can be helpful if you have like a UUID that's being generated. It's like, you know what, I don't care what exact ID is generated in here, just confirm that a string is passed in here and that's good enough. But it is good when you do know the exact value, it's good to test for it. That tends to lead to more robust, accurate, understandable tests. So let's jump into the code to see an example of using Jest functions in action to confirm that the output happens here, that this function we passed in is called MockOut. And again I am gonna thank you for the chat, everyone, and I am gonna check during our next exercise section, I'm gonna check the chat notes there. Maybe before, right at the start, when we start that exercise I'll check there to get back to any questions you have. So we tested one scenario here, we tested that when you fill out the form it clears the message field. But that's kind of secondary, really, the most important thing we wanna check is that it calls the onSend prop with the inputted text. This is not how I wrote this in my notes, but that seems like a more descriptive test name to me. So for now, to start out, let's copy and paste this setup, and then actually after this we'll show how to kind of remove that duplication. We still wanna get, oh, we need an async function here. We still wanna get user event setup, we still wanna render out the message form, we still wanna type in and click the send button. But now what do we wanna check? Well we need to do something a bit differently. To call the onsend prop, we need to pass an onsend prop. So let's set that up. We'll call it sendHandler, just.fn to create a mock function. And we'll give it a name and we'll see, we'll remove this later to see the difference that makes. And then we pass it in, the onsend prop, give it the sendHandler. So when that's provided, when we click the send button, we want to confirm the sendHandler to have been called. And in our case, we specifically care about the value. So let's pull that up into a variable here. const messageTextHelloWorld.

18. Testing the New Message Form

Short description:

We can confirm that the sendHandler is called by testing different scenarios, such as not calling the onSendHandler at all, calling onSend without arguments, and passing a bad argument. The testing library ESLint plugin provides helpful recommendations, such as using calledWith to specify the arguments passed to the sendHandler. It's also possible to check the number of times a function was called using calledTimes. In my experience, checking the arguments is usually more important than the number of times the function was called.

This way we can type in the message text here, and we can confirm that that same message text is past the sendHandler. I could have just copied and pasted the string, and sometimes that is more beneficial, but in this case, I think I kind of prefer having a variable. As you can see, hey, the same thing that's inputted here is outputted here. Same thing the user types in is passed to this function. So let's save, and the test does pass. And of course, we don't want to trust it. So let's break this in various ways to make sure this is really confirming that the sendHandler is called. First of all, let's not call the onSendHandler at all. Let's just comment that out entirely. But when the form is filled up, yep, I've been called with message check. So expected to be called with Hello, world, but it was called zero times. Okay. Instead, let's have onSend called, but not pass any arguments to it. What happens then? It was called one time. We expected it to be called with Hello, world, but instead it was called with zero arguments. So that's nice and descriptive and we type in a bad argument and pass the string bad. Now the test says, oh, expected to be called with Hello, world. Instead we receive bad. There's a testing library ESLint plugin that provides a lot of help and recommendations for your React testing library. And one of the things that it will do, actually it may not be a testing library, it may be a Jest. If you said expected to send handler to have been called, it's going to warn you. I don't have it set up here, but it will warn you and say, hey, it's probably better to do called with. Like you should probably specify the arguments pass. And even if you aren't passing any arguments, you can say called with empty argument array to confirm that it's called with no arguments. Usually if you're calling a function, you probably care about the arguments that are being passed. So, it's a good idea to call two have been called with. Nan in the chat says you can check to have been called times. We can confirm it's called one time instead of two times. I'm not aware of a way to check both the number of times it was called and the arguments that are called as well. Usually in my cases, you can do both. You can do one after the other. At times, confirm it was called one time with these arguments. Let me fix it here so we have the right arguments here. You can do both. That is the most robust. Usually, for me, the arguments it's called with is more important. Sometimes something is called several times and I want to make sure. There's a risk of it being called too many times. React 18 calls to APIs. Anyone use effects? That doesn't happen in tests. Forget that side trail. I often reach for it to have been called with.

19. Removing Test Duplication

Short description:

We have a lot of duplication in our tests, but we can remove it by creating a common setup that's needed for both tests. By pulling out the message text and using it instead of a hard-coded string, we can eliminate duplication while keeping the tests passing.

Now the next thing is sort of a side note here. We now have this test. We followed that approach I recommended of testing one behavior per test. Now we have a lot of duplication. What can we do to remove this duplication? It's not identical. There is a setup where we get the user. We render out. This is duplicated exactly. What can we do? We can pull out. I would recommend don't pull it duplication right away. But when you find it's duplicative and makes it hard to read your test, it's good to pull it out. What can we do? First of all, there's things that are different in these tests, but they don't need to be. We're going to create common set up that's the set up needed to get both of them working. The fact that some don't need some of the details isn't too important. Let's do this one bit at a time. It can be helpful to do refactorings and keep the tests passing. Let's pull out message text, put it in the describe block and use it down here instead of the hard coded string. Let's put in message text there. We save and the test still passes.

20. Jest Setup and Mock Names

Short description:

Jest provides a before each function for setup. It's recommended to have an explicit render function call. Mock names can improve test readability and prevent misunderstandings.

Another thing we can do is we could put a user in a let variable up here. That won't make sense until we pull up the set up code. Let's do it this way instead. Jest does have a before each function that you can use to perform set up that runs before each of the tests down below. There's a Lint rule that recommends not calling render before each. It makes it not so obvious in your test that rendering is happening. Instead, it's better to have an explicit function that you call. That's the approach I'm going to show here. Render and fill out. I like using the word render in the set up function just so we can see at a glance rendering is happening. I'm going to copy and paste the setup up into there. You can do all this setup and then it should be an async function because awaiting is happening in there. Then down here, we're going to say, await render and fill out. We're getting some ESLint errors. We don't have access to the send handler here. There's a few ways you can handle that. You can return the same handler out of your setup function. In this case, what I'm going to do, that will work fine. I'm going to declare send handler out of here. In the render and fill out, I'm still going to assign it. Now, send handler is now available because it's available up there and it's available down there. Now when we save, the test passes. Now let's use the setup function for the other tests as well. Render and fill out. Await render and fill out and then we remove it. Then we get a, Select screen, get labeled by text message to have value empty string. We save and it works fine. Because user event is not needed down there, we don't need to put that in a variable accessible elsewhere, we can keep it in render and fill it out. Now this line wrapping makes it hard to see. But now our test, we've removed most of the duplication. The only duplication is this common call of this render setup function. The before each approach removes even that, so it's just the expectation in the test, but React testing library recommends and I'm good with going with that recommendation. It's a bit clear to have this explicit call to render. Certainly if the setup gets longer, or if your tests get longer, it can be nice to see at a glance when you look at the one test that's failing, ah, I see that the first thing that happens is we're rendering and filling out. So now you can explicitly, you can even go to definition with your IDE or intelligent editor to go up there. It's a bit less mysterious. So that's a recommendation from the Linter that I'm happy to go with. When you want to, this can be a way to remove duplication from a test. It allows you to have the benefit of these nice, simple testing one behavior per test. It gives you nice output ingest as well. So you can see a description of the behavior of the component, a description of the contract of the component. So you can see when the form is filled out, it clears the message field, and it calls the Onsend prop with the input test. That's a helpful documentation to read if you're a developer newly working on this component, to see what's going on, to see what the expected and current behavior of the component is intended to be. And again, and just to reiterate, I don't always remove this duplication right away from the very start. But anytime the tests start to feel cluttered, I will remove duplication like that. And I find that's generally useful. So let's talk about, oh, I need to show you the one thing that I promised you I'd show you, MachName. So what is the purpose of MachName? What happens if it's not there? Well, if I remove it, when the tests are passing, everything is just the same. Nothing is different. But let's take a look when the test is failing. Let's comment out that onSend again. So check it out here. When this fails, it says expected, and I didn't I didn't point this out before, expected send handler to have been called with expected. Expected hello world number of calls, 0. And then it shows the code down below. Expects send handler to have been called with text. What happens if I remove mock name and save. Now it says expected just function to have been called with expected. Expected hello world number of calls, and the code down below. So send handler still shows in this code snippet down below. And just is able to output the source code of the test that's showing here. But the first line here it says expected just function to have been called. Now similar to a question that was raised earlier about SVGs. If we only have one mock function here as we do here, maybe that's super obvious. And we're thinking about this test, and I'm talking about it at length, so you have this all loaded into your head. So you know the mock function we're talking about. So maybe in this case it's totally fine. I have other code though where there's an object with maybe four or five different mock functions. And I want to confirm which one is being called. This is common with API clients or API.get, API.post, API.patch, things like that, and so in that case it helps readability at the very top of my test to be able to see expect send handler to have been called. It's test readability that helps your test fails to save you time and to prevent misunderstandings. I like that little bit of extra readability, so I tend to put in mock names. It won't hurt you immediately if you don't, but if you find that you're in a situation where you've got a mock and it's failing, it's kind of an indirect concept mocks anyways, and so if you find that expect Jest function to have been called is confusing or leads you down the wrong path or wrong assumption, adding mock name to your Jest mock functions I found can be very helpful. So that's why I prefer it and recommend it.

21. Testing Jest Mock Functions and Exercise Two

Short description:

We confirm that the send handler is called with the right message text, testing the contract of the component. Testing a component involves choosing to test one part and not another. If we were not testing the on-send function for a new message form, we would not be testing the component's contract. The user doesn't care about the implementation details of a component. Testing with a Jest mock is appropriate when the prop passed in is part of the component's contract. End-to-end tests provide the most realism, but sometimes it's necessary to test just the React application decoupled from the backend or just a component decoupled from the rest of the application. Exercise two is similar to the previous one, where we press send, clear the title field, and call the create handler. We use the render and save async function and set up the user event variable inside it. By explicitly passing variables along, we avoid duplication and ensure that tests are testing the same thing. The explicit approach is encouraged when sharing mutable variables across multiple tests.

All right let's summarize what we saw about Jest mock functions real quick. So here's our calls the send handler with a bit of a different test name. This is before I pulled out that duplication, we set up the message text but then we create the send handler, we create the Jest mock function and then we pass it into the component. And after we interact what we're confirming in this case is it's not visible output to the user, it's a function call, we confirm that the send handler is called with the right message text. That's the contract of the component, what it's contract for the rest of the application is if you pass in an onSend prop I will call it at the appropriate time that's what the component guarantees the rest of the application and that's what we're testing here.

So one thing I want to say here to talk about the idea again of implementation details. So someone could say, and actually there was just a question a week or two ago on Stack Overflow where someone asked about this, you know the user doesn't care about an onSend function, the user doesn't care that the send handler was called. So are we testing implementation details by using a just mock function and checking that it was called? Now here's what I would say about that. The principle test like a user makes sense in general and I don't disagree with that but whenever you're testing a portion of your application you're choosing to test one part and not another part. And if you're testing a component you're testing that component as it stands. So what would we test if we weren't testing the on-send function for a new message form? Maybe in your code of your application you do have you wire it up and you pass an on-send function in and it saves something to Redux or saves something to Apollo GraphQL or saves it to a server. But if we test that we're not testing and you might have written the new message form to hard code that in. Maybe new message form pulls in the API and calls it directly, but we chose in this case that new message form has a prop passed in for on-send. So as written the new message form component does not know and in fact it's reusable it could be used in different contexts. It does not know. So if we are if we've chosen to test a component rather than test our whole application we have to work with the component contract and the contract the component is. Hey, rest of the application you give me an on-send prop. It wouldn't make sense in our test to wire up new message form to the API here because that's duplicating code that's elsewhere in the production code. That's not part of the new message form's contract. If we want to document how a new message form is written and what it's written to do, calls the on-send handler is exactly what we want to document. That's the contract there. That's why I really teach this idea of testing the contract and thinking about the inputs and outputs of a component. Sometimes the input and output of a component is other code. That's part of it. In fact, I would say just to stay on my soapbox a little bit longer, the user also doesn't care that there's a component with new message form that's rendered out using JSX. We are already in the realm of something the user doesn't care about when we're testing components. I'm not saying that as a criticism of component testing, I'm saying there's a tradeoff there. End to end test with a tool like cypress or similar tools are the most realism in terms of they're running a full application and they're letting interacting like a user. In fact, to be fully realistic, you need to connect to a real backend, maybe a real credit card processor. Maybe you don't want to do that on your automated test that runs every time you save a file. So there's realism that is good in some cases, but sometimes you don't want the full realism. Sometimes you want to test just your React application decoupled from the backend. Sometimes you want to test just a component as decoupled from the rest of the application. And just like once you're testing a component, you've brought in the fact that you're importing a file from a specific location and you're using JSX to render it out in the same way. If that component is set up to take in a function prop, that's part of the contract, the inputs and the outputs. And so testing with a Jest mock is an absolutely appropriate thing to do in that case. So I'm still working on how to explain this well. Maybe I'll do a blog post on this someday, but that's why I would argue that, in this case, when the prop passed in as part of the contract, the external interface of this component, testing that with a Jest function is not an implementation detail. That's absolutely testing the contract of the component, which is totally an appropriate thing to do.

Let's talk about exercise two. And hopefully if folks are running behind a little bit. We'll catch up as we go. I actually tweaked my answer a bit to experiment with some of the things we were discussing in the chat. There's definitely multiple ways to organize the code for this one. Let me show you what I've got and field any questions or feedback from you all. I'll stop the field of that chat before we get into lecture three and part three. So I am not a very original exercise maker. This form is so extremely similar to the message form from before. It's similar situation. We press send, it clears the title field and calls the create handler. Both of those things happen. Let me walk through what I set up in here. I did put movie title, the string, out as a constant at this level. I removed anything else at that level because I wanted to try out how that worked, but this, as a string, it can't be changed and is not intended to be changed. So I felt safe putting that out there. Then I have my render and save async function. I set up the user, user event variable inside there. One reason is I don't need it outside of that function. And another reason, I checked the user event 14 docs and they didn't, I couldn't find a place where they specifically said make sure to set up a new variable for each individual test, but that seemed to be the pattern that I saw. I didn't see them, like, setting it up once and using it all the way through, because I think then you could just use the old approach where you didn't explicitly call set up. I set up user inside the render and save function. Something I didn't mention before, I forgot to is, in our set up function here we set up the create handler mock function even though we're only asserting against it in one test. It's not needed for the other one. The other test doesn't fail when it's not provided. But when it comes to shared set up it can be useful to say, this is what I need to set things up in general, even if not every test asserts on every single thing, the shared set up can still be useful. I type and I click as before. And so this time I decided to show the approach where if you don't want any mutable variables in a shared context any mutable variables shared across multiple tests. So what I did is I created create handler inside the render and save function and then I return it. So this is the approach if you don't want to have those let variables at any level. If you have a set up function, you're going to need to return the things from it that are used elsewhere. And so that's not needed for movie title because that's immutable. But create handler, I return it. So in the case of clears the title field, I don't need any of the return values. And so I just await it. But in the case of calls the create handler, I await render and save and then I extract the variable that I need. Which in this case is create handler. So that's a way to explicitly pass things along. Sometimes I hesitate on that because it's like duplication in terms of like I got to create it. I got to return it. I got to pull it out here. And it's less code to write if I just have that let variable up above. But also, it's harder to see where it comes from. It is more explicit here in the same way to see oh, this create handler comes from here. It comes from this function. I can go up there. You can avoid a situation where maybe multiple places in the code are writing into it and causes your test not to be testing the same thing you expect it to be testing. So there's certainly benefits to that. My team on a React native project that I was on, encouraged me and encouraged us to take this explicit approach. And it worked all right. Before the screen API in React native, that only made it to React native very recently, you had to pass, you know, you had to destructure all of these getters from the render function.

22. Organizing Tests and Testing Effects

Short description:

Different ways to organize tests and optimizing for readability, maintainability, and avoiding false positives or negatives. Consider splitting tests for easier pinpointing of errors. Having one test per behavior can be more descriptive and cover all functionality. Speed implications of large tests and the importance of maintainability. Communication and understandability value of having one behavior per test. Consider displaying test cases in a spec sheet for stakeholders. Discussing effects and the trade-offs of hitting the real API versus mocking it out in tests.

So passing those along became pretty tedious. But you could also assign it to a variable called utils, et cetera, et cetera. There's a lot of tradeoffs here. The point is, this is all about different ways to organize the same tests and you're optimizing for readability, optimizing for maintainability, optimizing for, you know, not false positives or false negatives. And certainly removing let variables can make them more understandable and predictable. So I'm all in favor of a team taking this approach.

One thing to be aware of is if you say, oh, I got to return three or four values and that feels tedious. So I'm just going to write one big function instead and test all the things. Yeah, you can do that. Those variables are just available in that one test function local scope in that case but then you have a test where a lot is happening. You don't get this nice output documenting what the separate behaviors are of your test. And that's possible. That's doable but just think about what are you trading off when you have very large test functions or medium-sized functions and just make an intentional choice.

I'm checking the chat now. The test body is practically the same for test calling the on create handler and cleaning input. How valuable is splitting the test into two? If something bugs out, it allows you to pinpoint the error easier or faster. I would agree with that. If you have multiple assertions in one test, Jest will show you the line it fails at. One downside is that I'm not against that. That can work. I do write tests that have multiple assertions in them, even a sequence of multiple things happening sometimes. One thing that happens there unless there's something about it that I'm not familiar with, the first time it fails, the rest doesn't run. You just do one failing test in your output and you may not know, of the ten other expectations that are run, two of them fail, but eight of them succeed. If those were separate tests, you're testing one behavior per test, you'd be able to see these two are failing and those eight are passing and see that at a glance. Another thing I find is it tends to be more descriptive as far as, I find for me, having these one tests per behavior helps to really specify all the different things that Kimon does and helps me to make sure I'm covering all the functionality. When I have one big test that says, it handles submitting the form, it does five or six different things. It's hard for me to write a test name that covers all that. I just say, it can submit the form. Hopefully I make all the expectations in there to cover the behavior of the test. It's easy to say the test is long, it's probably good enough. It's probably testing enough. I might not be asserting and specifying all the functionality of the component. If I'm disciplined, maybe I can. I like to put approaches in a place that can aid me and keep me accountable to be disciplined. There is certainly a speed implication to having this. If you have a large React application, I don't know. Different approaches. I feel like if doing large tests that are testing a lot of many things at once, is that going to bring my test speed down to something maintainable? Maybe. Maybe for now. Maybe it's going to get big, larger regardless. Handling the speed issue sooner rather than later would be better. Again, I would really recommend folks to think about not saying you always need to do it the way I'm recommending. Make the right choice for your project. If all of your test functions are large and have many things going on, how maintainable are they? How easy are they to read? I can see at a glance here in this test. Of course, these tests are extremely trivial. Even thinking about this test output that says the two things that happen when you press send versus this test output, it's like, can I see at a glance what's happening here if this is just the one test that was here? I'm a fairly experienced programmer. I don't really want to have to parse through this, like, handles submitting. I'm going to tend to be too lazy to read through this and figure out, oh, these are the expectations. These are the two things that are needed. To me, clears the title field and calls the create handler is easier to follow than parsing out these two different expects and understanding what's happening. Again, I'm not saying you have to do this way, of course. You are totally free. But that's the tradeoff that I see. And this is a very simple task. If this was a much longer, if this was 20, 30, 50 lines long, it's a lot harder to read through it to understand exactly what the behavior is that's being asserted on. So, I would say that there is communication and understandability value to having one behavior per test. It's an option to consider. So, if you're going to big tests that make many assertions for them, just know what you're trading off so that you can make an informed decision with the rest of your team. That's all I'm saying. I'm catching up on the chat now.

Having the test cases and the actual spec sheet for the functionality of the component is pretty handy, especially if you display it in a storybook for stakeholders or QA-UX product owners. That's cool. And I will say, I mean, for the sake of transparency, like I haven't done that. It's really only been me that's been looking at the test output. But there could be team setups where you're really intentionally saying like what are all the things this form needs to handle? And maybe that communication is helpful for your case. Cool. Thank you for the interaction, everyone. That was great. Let's keep moving on, because we have less than an hour left. I want to get through this last section to get you something helpful. And I'm opening IntelliJ accidentally. All right. So we talked about different types of external function and what to do for testing with those. We handled function props. We're using Jestmox for those. That's one part of the ‑‑ a lot of movie series, the last part is split into parts that are longer. We're pulling a twilight or a hobbit here. We made partial progress, but there's more. What about effects? Let me say what I mean. So for external functions, you might have a function prop that's passed in, but also it's very common in JavaScript to have functions statically imported from other modules. And one example of this is hitting APIs. I'm addressing multiple things here at once. What do we do? Do we want to hit the real API? Maybe it's good. Think about that principle, testing like a user. The user doesn't care if anything happens internally, even within your react application. They care that the right thing is saved to the ‑‑ they don't care about the server, but their data is safe. Maybe we do want to hit the real API in our tests. What I would say about that is if you look at Cypress's documentation for the end‑to‑end testing tool, they have great information about the trade‑offs on integrating with the real back end versus mocking out your front end. And there's pros and cons. They recommend doing both at the end‑to‑end test level.

23. Mocking Web Service Requests

Short description:

To prevent component tests from hitting the server, mocking out is recommended. There are various options for mocking web service requests, including jest module mocks, library-specific mock-in adapters, and tools like nock and mock service worker. Jest module mocks are a general-purpose solution that can be used for importing modules locally or from third parties. They provide flexibility and ensure that you always have an option for mocking, even for legacy code libraries. In the lecture sandbox, a simple data loading component is used as an example. The component makes use of an API module that wraps Axios for HTTP requests. The recommended approach for data loading is to use frameworks like Next.js or libraries like React Query. However, for testing purposes, the choice of data loading approach is an implementation detail that can be changed. The test in the lecture sandbox confirms that the component loads widgets upon first render. Asynchronous behavior and the delay in server response are important considerations when testing components that make HTTP calls.

So what I would say is if you want your test to hit a real back end, end‑to‑end is probably the level of the application you want that to happen. Probably you don't want that to happen in your component tests. Very, very typically you're your unit test, I recommend running those on a continuous integration server and blocking the merge of your pull requests to make sure that they pass, and that they flake out sometimes that you address that to get them passing reliably. That's a really important thing for safety. But you can't have predictable, reliable tests that run on CI and block your merge. I would say if you're hitting a real API. Maybe some of you have done great engineering to make that happen, but generally that's not worth the effort. So I would say at a component test level, I would recommend mocking out and not hitting the back end server.

So now we have to say, okay, how are we going to prevent our component tests from hitting the server. Again, just to get on my soapbox for a second. Note if we're not hitting the real API, we're already not fully testing like a user. We're not fully testing realistically. So that door has been open. Now we have to weigh the tradeoffs. So there's actually a lot, a lot of different options for mocking web service requests. And I want to lay out the different options for you. I'm sure there's many more, but I'll lay out some of them and talk about which is the one that I teach in this workshop. So think about your app is organized this way. The rest of your code calls, maybe an API client module. Maybe you have a module, a JavaScript file where you set up your API client. And then maybe that calls through to Axios, the HTTP library. There's analogous things. Incidentally, if you're using GraphQL, there's analogous things in that setup, but we're just going to show this approach here. Axios uses underlying HTTP request support from the browser. And then ultimately, it goes out to a separate web server. So actually there's options to mock all the way along the chain here. So if you want to mock at the level of when you go from the rest of your code to an API client module, you can use a jest module mock, which we're going to see shortly. If you're Axios, you can mock out with a library-specific mock-in adapter. There's a different one that my current project uses that I forget the name of it. So anytime you're connecting to a specific third party library, there can be a mocking library specifically for that. When you're mocking out underlying HTTP request support, nock is one library option. Mock service worker is another popular one. When you're in Cypress, they have their own API for mocking. And then, even at that level, there's even other options. You can have all your JavaScript running as usual, but instead of hitting a real separate web server that lives out somewhere, you can have a local web server that's booted up on your machine or on continuous integration. Or you can have a mock web server, a server that's not a real one, it just has some hard-coded or configurable responses. When they post this, return this fake record back. But at that point, that's a mock that lives outside of your application and all of your JavaScript is running the same way. So, there's a ton of different options here. I want to talk about, let me see my slides. I want to talk about just module mocks. And let me talk about why. It's very easy in testing to say, hey, I'm using Axios or I'm using Apollo GraphQL or whatever, or I'm using Firebase. How can I mock that out? And it's like, all right, well, we got to find a library-specific mocking approach. And did someone create it? And are they maintaining it? And is it a usable API that's helpful and understandable in my test? And so, it's like, anytime you want to test something, it's like, I got to do more work to figure out a mocking approach or just hit the real thing or just give up on testing. And people give up on testing because this stuff is hard. What I love about and I'm not saying I only use gesture module mocks, I use others. I use knock on my side projects. What I like about gesture module mocks is that they're a general purpose solution. If you're importing something from another module locally or a third party, you can use a gesture module mock. So, that's a catch-all. And I'm sure there's exceptions, but like mostly whatever weird, crazy, legacy code library somebody is interacting with, if there's an import, you can do a gesture module mock. So, I like to test that because all these other libraries, hey, check out their readmes, it's like you can totally use them. But for gesture module mocks, if you learn that once, you will always have an option for mocking. You will never be stuck because nobody created a third-party mock or it's poorly maintained. And I want you to always have an option because I want you to always be able to write a test for something so you can get the coverage that you need and the safety and the protection that you need. So, that's why we're talking about gesture module mocks. Not because it's the best option, but because it's the most general option. And so, have it in your tool belt along with these other things. So, gesture module mocks. Those are mocked. Those are described in the jest docs. And let me show you what they are in the code to describe what this means. This is exercises. We want to be in the lecture sandbox. That's here. So, let's describe what we have here. In lecture three, we have just a very simple data loading component here. We're loading, we have an API module we created. I'll show you that. It just very lightly wraps Axios, but it gives us our base URL. So, we import that in our component here, and then in an effect we load it. Now, we know that React has been talking about use effects for data loading and the React beta docs, if you haven't seen them, are super, super helpful in this regard. It is recommended by the React core team to do data loading using a framework like Next.js or a data loading library like React query because there are a lot of edge cases and those libraries handle them in a very helpful way. I'm using use effects as a simple illustration, the simplest approach. In fact, one of the nice things about the approach of testing that we're going to see is that the approach you use to data loading is kind of an implementation detail. So, once we write the test we're going to write, you can refactor it to use React query. I actually have, oh, I don't have it here. But I actually have tried refactoring the code to use React query to confirm that it actually works. So, it's another example of tests that in this case, the exact React library mechanism to do the data loading is an implementation detail and tests allow us to change it. So, let's take a look at our test here. We want to confirm it loads widgets upon first render. So, setting aside, in testing details, we want to render the component, we want to confirm that widget one and widget two are present. So, when we run this, it says we're unable to find an element with this text. Let me get my notes up here. The element is not found. Now, we haven't done any mocking yet but still, like, this component hit a server so and there's no auth, so it should be coming back. So, what is going on here? The first thing to know about and learn about is about asynchrony. This component is rendered and like the web services the web services is not replied right away. We're making a real HTTP call to the real server, it cannot respond immediately and React wouldn't re-render, if so.

24. Testing Asynchronous Code with Jest Module Mocks

Short description:

When testing asynchronous code, console logs can help understand the sequence of events. However, relying on timeouts and server responses can slow down tests and lead to failures. An alternative is to use Jest module mocks to replace API calls with controlled responses. By mocking the API module, we can control the return value of API.get and ensure expected behavior. This approach avoids reliance on external servers and allows for faster and more reliable tests.

So, we can confirm. Whatever asynchrony is involved, I can use console logs to understand what's happening. Let's say console log sent request and console log got response. Let's see what happens. When we save, we see that the request is sent and then the test fails. And actually, check this out. It says, cannot log done. So, not only do we not see the response outputted, Jest is letting us know, hey, something happened after your test finished. So, we need a way to wait. So, one way we can do this, just to see that your different options, I wouldn't recommend this, but you can use a promise. So, you can return a promise from a Jest test. There's a done approach, but this. This is just for illustration purposes. So, we've got a promise here. We're going to resolve at the end, and actually, we'll do a set timeout. So, you could have a timeout where you say, I'm sure I haven't typed this right. So, yes, in the test, we return a promise, and Jess is going to wait for that to resolve. We have to build it from scratch, and then we set a timeout that says, let's wait for a thousand milliseconds for a minute before we check for widget one and widget two to be present and then resolve after that. So, that gives the test time to wait for the response to come back, and then notice here we get sent requests and got response earlier. There's a warning here we're going to ignore for a second, but the test is, in fact, passing. So, we are actually waiting for these responses to come back from the server, and then let me just break the make sure the test actually fails. Yeah, so it says, there's a lot of output here. If we find, I'm able to find an element with the text widget hi hi. So, that never comes back, but widget two is, in fact, found. So, we are now, we're not waiting for the server to response. We have an option. So, what are some of the downsides of actually hitting a server and waiting like this? So, if the server takes too long, the test can fail sometimes, and that actually happened in the last workshop I was in. I had to bump up the time from 1,000 milliseconds to 2,000 because I wasn't on, I was on a slower internet connection. So, as a result of this, though, this slows down your whole test suite. This test is not done until those milliseconds are done. And so also if the, so that slows in your whole test suite if you're adding 4,000, 5,000, 10,000 milliseconds to many, many tests. And old style end to end tests ran into that a lot and the test got slow. Talk about slowness. Setup, not very slow. Having every one of your tests waiting multiple times for 10 seconds, that's extraordinarily slow so you don't want to do that. And also, if the remote server goes down, your test is going to fail. So, what is an alternative? We can use just module Mocs to replace the API call with one we create that we can control more directly. So, first, let's restore our tests to what we did before we added the promise and get back to that situation. I've got an extra curly there, so let's put that back. We've got that logging happening afterwards as well. Let's look at how jest module mocking works. First, let's do this. Jest.moc slash API. This is not a JEST mock function that we're creating from scratch. We're saying, Jest, please mock out this module. It's the API module that's available from the same folder. I looked at the other API file, ignore that one. It's this one in this folder. Let's just save that change and see what that change is in and of itself, because it's indirect and it's good to understand it. When we call Jestmock on the API module, now it says in our code it's saying, when we call API.get widgets, that happens here, it says, cannot read properties of undefined reading then. What this is saying is we're still able to call API.get, but it's no longer returning a promise. We can no longer chain a then on it. This is because Jestmock, there's details of all the different ways you can do named and default exports that I can't summarize very well. In general, a Jestmodule mock mocks out all the functions that are exported out of there, including in this case, the different functions that are part of this API object. API.get is available, and it just returns undefined by default. Now, we can control it, and we can give it the response that we want. The first thing, we want this to succeed, so we want to get access to API here, and we can say import API from API. We're doing the import just the way we do in the production code, and what we get here is the mocked out version of API. We can say here API.get.mockresolveValue, this works on all JS mock functions. If we create it from my hand, or we create it with a JS module mock. This says, hey, when API.get is called, return a promise and have that promise resolve. This should allow us to call a.then on it. Let me save and show what this does. Now, we get an error, cannot read properties of undefined reading data. That's different. We've gotten past the.then, and we're getting into here, and we're calling response.data. But it's saying that response is undefined. And that's as we said, API.get should resolve, but we're not saying any value that it should resolve with. By default, it resolves with undefined. We want to give a real resolved value, the value that we expect to come back in this response, into this promise. So, we can do something like this. We can say mock resolve value data, because with Axios, the response object has a data property, and that's what it goes into. The data property is an array. We can say, name widget one and, do two, a second widget. We're controlling, hey, the API.get should resolve with this data. That should be the data that satisfies this test. So, let's save. I have an extra curly there. Oh, and I notice my Jest runner completely errored out that time. I'm not sure exactly why, but that failure was enough that it couldn't recover. We're going to start the test runner again. Now we have a new error. Unable to find an element with the text widget one. That's surprising because everything is proceeding along. We should have the widgets. We can log them out, but why aren't they shown on the screen? We can see the DOM output here. Again, the value of this clear output from the get by, we see there's the body, div, the list which is this list, but no list elements are rendered. It's like no widgets are available. So the reason for this is because of the way React re-rendering works.

25. Testing Asynchronous Rendering

Short description:

The reason for this is because of the way React re-rendering works. Promises resolve asynchronously, and we need to give React time to render components that may not be immediately available. We can use the find-by function to wait for the component to be found and confirm its visibility. This allows us to test the rendering of components that rely on asynchronous behavior.

So the reason for this is because of the way React re-rendering works. This promise is set up to resolve right away, so there's not a number of millisecond delay. But promises resolve and I will get the details wrong, because there's things about the JavaScript event loop and microtasks and things like that, but basically, you know, it doesn't resolve immediately. Like, you know, JavaScript is running, it's single threaded, it gets to the end, and then it promises resolve. Time for me to kick out a new thing that triggers a React re-render. There's a little bit of asynchrony needed, even though there's not a weight of 1,000 or 2,000 or 10,000 milliseconds. So what we need to do is some way to give React time and say, hey, this widget won't be available right away, wait for it to become available. And the easiest way we can do that is to make this an async function, and we can use what's called a find-by, instead of a get-by. The difference with find-by, it returns a promise. It retries it until it can be found. We do this in the middle of the expect, we're waiting for this widget 1 to be available, and we confirm that it's visible. So this await find-by is going to give React time to rerender. Let me scope down to lecture three, because I think that was a failure for one of the other tests. We got sent request, response, and the test is passing.

26. Mocking Backend and Asserting Outputs

Short description:

When mocking the backend, we have control over the data returned and can handle different scenarios. It is important to assert on the output of the server call to ensure the correct endpoint is hit. Data coming back from the server is an input, and what is rendered out is an output. It is crucial to confirm that the output of the server call is correct. The Knock mocking library combines these aspects, making it easy to define the data returned when hitting the server. Let's go back to the slides and process what we just saw.

As always, let's not test it. Let's change the data to make sure it fails, where if the API returns the wrong data, it will be rendered out wrong. Another nice thing about mocking out the backend versus hitting a real one is you can control the data. We're controlling, here's exactly what's returned. That makes it easier to handle different data scenarios. You can have this reject to handle error scenarios. So you get a lot of control when you take any of these mocking approaches within the JavaScript codebase itself. When I save here it reruns and I get the error unable to find an element, we can see that widget 1 and 999 are rendered out. So we've seen the test fail the way we would expect. If we do pass in widget 2 correctly, it should be cleared out correctly. Notice that I didn't change the second one here to use a Wait to Find By. And that's because once the first one succeeds, like we wait for widget 1 to show up, and once widget 1 shows up, widget 2 is ready. We don't need to wait again. At some point in the past, these tools, if you had two awaits in a row in an unnecessary way, you'd get a warning. I think that might be fixed now. So let me try that out to see if I don't care whether there's a wait or not. I just care that eventually they both show up. And that does in fact work, yeah. You could do this parallel to see if, and this way it just says, hey just make sure that both of these eventually show up. But if you do want to say, hey, I'm confirming that as soon as the first one is there, the second one is there as well, doing a get by for the second one works, because in the way Async 08 works, once we're past this line, we've now waited for this to happen, and this happens synchronously right away. It checks right away to make sure that widget 2 is available. And we can remove our log statements here to clean up our output. Now we get to the point where we're confirming that we're loading the widgets upon the first render. And now there's no wait at all. There's no set timeout. There's no number of milliseconds. This asynchronous loop needs to happen once here. But we're proceeding as fast as the React Asynchrony will allow us to proceed. There is one more thing we need to test though here. This is actually one of the downsides of using just ModuleMox that other approaches like NOC and MOC ServiceWorker handle for us. What if we got the URL wrong? What if we... because we just said here when you call API.get MOC resolve value. So what if instead of widgets, we said fidgets? What if that's not the correct URL on the server? We save and the test still passes. And we don't want it to. We want to say, hey, I want to make sure it's the widgets endpoint on the server that returns this. So we can make it... confirm it. So there are more complex ways to do implementations of just MOC functions that could like check the arguments and stuff like that. But one straightforward way to do it is you can make an assertion on the API.get. You can say expect API.get to have been called with widgets. We save this and it fails. It says, oh no, it wasn't called with widgets. It was called with fidgets. And we go back and save widgets here and then it's done. So this is an important thing to think about, because with... although there is... yeah, really with any of these MOCing approaches, oftentimes it can be easy to say... to be insufficiently clear to say, hey, as soon as a get request is made to the server, return this data. As soon as a post request is made, return this data. Or like as soon as a GraphQL query is sent, return this data. And that could get your test to pass. It could prevent the issues where just there's no data available there. But it might be too general. You might not be asserting on all the inputs and outputs. Think about it this way. The call to API.get is an output. It's going out of the component to somewhere else. And do we care whether the right endpoint in the server is hit? Yeah, we do. Our application is not going to work if it's not the right endpoint. And so it's important to assert on not just that when the right data comes back... and let me start to say it this way. A call to the server is an output from the component, in a sense. Data coming back in response from the server is an input. And then what's rendered out is an output. And so it's not enough for us to say, when this data comes back from the server as an input, make sure that this is outputted on the screen. We also want to confirm, hey, make sure that the output of the call to the server is correct, as well. And that's in some sense, is even more important with data that changes things on a server. When you're saving a record to the server, you want to make sure that all the appropriate data that the user entered is sent in the right data structure to the server. Otherwise, your front end will work in and of itself, but it's not in reality. And it's not going to talk in ways that the server will actually work with. One of the things that I like about Knock as a mocking library is it combines these together. So you very easily say, when I hit the server, and call slash widgets, return this data. So I've started using Knock pretty significantly in my own tests because of that. I think that's a nice benefit. OK, we're ready to go back to slides. And again, for the sake of expediency, I'm going to keep going and then feel the questions in a group. I think, as this often happens for this version of the workshop, our last exercise, you may need to take that on your own. We may not have time to do it together. Although I will stay available. So let's go back to the slides and process what we just saw. So here's a Widget container, and let's go this way to see it. So when we're taking Jest Mocking, and I'll back up in a second and talk about general mocking approach because there's principles here that apply to any approach where you're mocking at the backend. But when it comes to Jest module mocks, and specifically, we import the module, because we're going to need it. And then we mock it out, we say Jest.mock out API and replace it with Jest mock functions. They don't have any behavior by default, so we need to and want to. This is exactly what we want to do with the mock, and we want to configure them with what the resolved value should be, what data should come back. Then, in order to wait for that, because there's a sacreny involved, in this case, there's a promise because it's related to a web service request that needs some time to come back. So we need to await. We need to await a find by to make sure to give it time until that comes up on the screen. We can't just proceed ahead.

27. Testing External Functions and Hooks

Short description:

We need to control what's going out to the server and coming back from the server and make assertions on the data. We have different options for mocking return values, such as mock resolve value and mock rejected value. React Testing Library provides different query functions like get by and query by to find elements and make assertions against them. Find functions are useful for asynchronous testing. When using hooks, we can test them similarly to how we test prop data and prop functions. We can also test hooks that call static module functions. The focus is on testing the unique functionality of the component. In the movie list exercise, we test the presence of movies by checking their names. We don't need to test all the details of the movie row component again. We can mock the API call and confirm that it was called synchronously.

We need to let the JavaScript event loop tick over. And then at the end, we also confirm the URL that's called on API.get. Now, although if you're using NOC or mock service worker or another approach, the syntax of here will be very different, but the goal would be the same. The goal is, I want to control what's going out to the server and coming back from the server and control it in an individual test and make assertions on what is sent out, what comes back, and what is rendered out on the screen. We're thinking about that component contract again.

Just to briefly show some of the options, these are available for all MOCs, but specifically, when you have a MOC module with a function in it, you can mock a return value. So that's useful. If it's not promise-based, it's not asynchronous. It's just, you're just mocking out a module that returns something right away. If you're mocking out a UU ID coming back and you want to control the UU ID value. You can just pass up your own promise and mock return value, but if you're using a promise, it can be more expressive to use mock resolve value or mock rejected value. That causes the promised that comes back to resolve or reject respectively. It's great for success scenarios and error scenarios.

Let's talk about those fine buys. So early in the workshop, we saw the get buys, that check for an element and they will error out right away if it's not found giving you helpful error output. We also saw query by functions and same prefix for all the different suffixes that will give you a null back if it's not found. And this is recommended to be used when you expect something not to be found because that allows you to make an assertion against them. Third, we have find functions as well. These wait for an element to appear and they error out if it doesn't appear within a certain specified timeout. They check repeatedly. And so you saw, you may have seen, we weren't waiting for an extended number of seconds for that fine by to resolve it. Very quickly resolved, but it'll wait up to four seconds, I think by default, to wait for something to come back. And that's useful in case you do have a set timeout in your production code. But find by is useful for this asynchronous stuff and you can wait for it. So this is an approach to first function statically imported from modules. And again, I'm conflating a few different things, but like, what do you do when you're importing a module? What do you do when that module has asynchrony involved? And what do you do when that asynchrony comes from a web service? We've shown a number of different tools together for how to handle it. Now functions from hooks. And I promise we're not going to extend this splitting up the last part of the movie series indefinitely. So I'm kind of glossing over hooks a little bit, but in a sense, React hooks are just a way of organizing code. And so how do you address when you're using a hook in your code? It's going to be very similar to the tools we've already used. So if you have a hook that gives you data or a function, maybe you're getting data from a context, or maybe you're getting a function from a context that is called into from your function. That's like a prop value of function. It's not passed in through props, but in your test, you can control what's in that context and what's coming in. And so that's inputs and the calls to functions or outputs. So in those approaches, those kinds of hooks, you'd test them very similar to similarly to how we have with prop data and prop functions. Or a hook might ultimately call a static module function. Maybe it calls that API client that you have or a third party client like Apollo. If that's the case, then you would test that hook in the same way you use as you test the module. And in many cases, if your code is pulling in a hook, it's recommended by React testing library, consider that hook and implementation detail. And that's kind of what I'm advocating here, basically is like, OK, you've got a hook going on. But what happens at the next level after that? Is that data from another context that's passed in? Is it a hard-coded third party library? And you can use all the approaches we've described above. Now, I don't always follow that approach. I'm mentioning something we're not going to have time to do together. I do sometimes test hooks directly when it's first-party code that is intended to be reusable. The Current Client Project, we're doing that significantly. But sometimes a hook is just a hook. A hook is just an implementation detail and your code doesn't need to know. It just needs to know when there's this data in the context, what do I show on the screen? When I fill out this form, what function in the context is called or what is sent out to the server? That's glossing over things a little bit, but I'm available in the discords if I've been unclear. Happy to dive in, but I think you'll find that the tools have been equipped with, like, they help you and the use of those will jump up as you're diving into hooks. So that's the different types of external function. I'm sure there's more I haven't thought about, but I think that covers a lot of the cases. And so now we've finally come around to handle the last output. We've talked about the inputs and outputs, testing a contract of a component, the props that come in and the user interaction events that go out. Oh, sorry, let's go in as well. The props and user interaction events that come in and the rendered UI and calls to external functions that go out. Let's take a look at the third and final exercise, and then we will go in to wrap up some final points to recap what we've gone through and leave time for any other questions or thoughts, suggestions that folks might have. All right. So, yes, some screen sharing. Good. So the movie list, loading movies on first render. I did do a test of creating as well, but I'm going to hide that just to keep us on track. This one is going to look a lot like the example we said. Again, I am not the most original exercise creator, but I wanted something else that was analogous because even just getting the basics of these things wired up if you haven't done it before or if you take a different approach to asynchrony, it's good to learn and try it out. So movie list. We also want to load movies upon first render. We are taking the approach here where we've got an API in just the same way. We're pulling it in, we're mocking it using just module mocks, and then we're setting mach resolve value as well. And again, as we saw in another test, I'm pulling in just enough data. So this is interesting. This is something interesting that falls out of here. We tested movie row, and I think this came into the exercise description, I said movie row has a title and it has the added today button. And so OK, movie list uses movie row under the hood. What should we test? Here I decided just to test enough to see that the movies are present by testing the name. I didn't pass in any added today properties on those objects, and I didn't check for that because we're testing movie row already. I didn't feel the need to test that again at the movie list level. You could and as you're don't mean to say this too sarcastically, the larger the larger your app gets, you could test all the details of it at every single level or just only test from the very start of your application, but that's a lot and that will slow down your app as well. That's exponential increase of every time a movie row is shown. I'm testing all the aspects of movie row again. I don't think that's useful. The focus here is on testing for movie less. What is the code in movie list itself? What are the unique things that movie list does? No, this test doesn't ensure that we're actually using movie row underneath it versus just putting the text directly. But I'm not. I'm not worried about getting that wrong. These are the kinds of judgment calls you need to make. Sometimes it might make sense to test all the details or maybe just one other detail of the movie row or maybe it doesn't. So, I mock the result value. I render out the movie list. This time, I actually put the API that get assertion above the weight just to illustrate that that happens right away. And so if that, you know, if this happened immediately upon render, or if it happens when you click something, for example, you can confirm that that just Mach was called because that is called synchronously. That call is called in the same part of the event loop.

28. Testing Movie List Level Create Functionality

Short description:

When testing the movie list level create functionality, it is important to ensure that the API.post method is called and that the data is sent to the server. For more details on this, you can refer to the solution branch of the repository. Let's continue with the workshop.

I guess if you're waiting a click then you're awaiting already. So that already gives JavaScript time to do stuff. But even here we render and then in the same event loop, we immediately check for API get have been cold. Yes, it has been called. The product, the promise just hasn't resolved yet. But we await for fine by to wait for movie one and movie two, to be there. And then we're good to go. We assert on them and they're present. Let me just get Discord pulled up as well. But yeah, so that's really straightforward. I don't think I want to jump into the deep integration test, but you can check this out in the solution branch of the repository later if you like. This is basically an approach for I'm I'm adding the new data which we tested in the form that when we add the data that on the create handlers called. This is testing at the movie list level create is hooked up, so we are calling API post. So if you're testing to create functionality of movie list. You are testing that API dot post is actually calls and data is sent to the server. So you can check that out if you like to see how a test like that of sending data to the server would work at that level. But I want to move on from there.

29. Testing the Contract and Conclusion

Short description:

Testing the contract of a component leads to tests that are higher value and lower cost. It's important to avoid testing implementation details and focus on the input and output of the component. Thinking about the component's contract helps determine what is an implementation detail and what the user is concerned about. Testing the contract allows for refactoring inside the component. I love talking about testing and would love to stay in touch. You can reach out to me through various channels and check out my book on test-driven development with React Testing Library. Thank you all for attending the workshop and giving me the opportunity to share my knowledge.

Let's take a look at the slides. Wrap up to summarize where we've been together today designing effective tests. You know, the hope was to introduce both the APIs of react testing library as well as an approach to thinking about them to test that are high value and low cost, because if you've ever been there, there's nothing more discouraging that tests that feel like only costs, they never catch a bug. They never ensure functionality. All they do is just cause you to have to do three times as much work anytime you change anything. That's frustrating. That's not helpful. But I'm hoping and I think and believe in my professional experience has been that if you take the approach of testing the contract of a component, you're going to get tests that are higher value and lower cost. It's still some effort, right. Writing tests still takes some effort, but they're going to give you value. I'm not trying to assert that you have everything you need for testing forever from this. Just the three hours. I really have found that testing is irreducibly complex and there's always more to learn. And as I've said, there's more in your specific context with your team, project, organization, and just yourself. There's things to figure out what works for you. But I hope that I'm giving you some helpful foundations. These are the things that I've found are the basics that I need and that I try to equip others with for effective tests. So what do we learn together? We talked about what does it mean to test the contract? This concept of thinking about the contract, it's not quite the same. It's similar to but not quite the same as tests like a user because a component is already down one level removed from the user. It's also not quite the same as don't test implementation details because what's an implementation detail from the user standpoint is not an implementation detail from the component standpoint. A component is interacting with other components. It has props, it has JSX, it has functions that it calls. If you are going to test a component rather than only doing end-to-end tests, you're already working with something that's not quite what the user interacts with, but it is testing the contract. Having tests like a user and don't test implementation details in the mix in your thought processes are absolutely valuable. I think test the contract gives you a specific way for thinking about what is an implementation detail in this case, what is the user concerned about, where the user might be another component using this component. We also talked about why test the contract. Um, let me, I got distracted here. Isegar says, can I find the solution somewhere? I'm not passing my test and would like to compare with the implementation. Oh yeah, Lotti said there's a solution branch. Yes. So if you pull down the solution branch and I should put that in the in the README if it's not already there to let people know, I just kind of hid it off of the main branch, but the solution branch is there that you can compare against. So please check that out. And I'm available on Git Nation's Discord, the conference Discord and other ways to be met. I would be happy to field questions from any of y'all later, if you spend some more time working on exercises and it's giving you any errors or issues or anything like that. We also talked about why test the contract, which I guess I just said, the coming back again. Um, testing contract gets you out an implementation details of what exactly is happening in the component. What exact style props are added? Can I get to the state to figure out the internal state of the components? Or what exact test ID is being rendered out. The contract is like what is what is what is the input and output? So what's the interface of this component to the rest of the application? And that allows you to do refactoring inside the component. Third, we learned what are the inputs and outputs of the component, and I'm sure there's others, and some of y'all sliced it in slightly different ways and slightly different ways. Those are all valid as well, but I really summarize it as props and user interactions coming in, and, um, rendered UI and calls to other functions going out, which includes calls out to servers. And I think that category can be helpful because it can be really overwhelming to think about a component and to say, this is something that I run into, and it's a very reasonable question. Folks will say, I have this line of code. How do I test this line of code? Or they'll say, my code coverage tool is showing that I'm not covering this line of code that's very valid. I want to cover it. How can I test this line of code? And what I really try to instill in people is that that mental model can get you stuck because then you can think about, how do I get to that line? How do I execute that line? And that can tempt you to dig in through the external interface and get to implementation details. But if you think about, what is the purpose of that line of code? What does it cause the component to do? If I comment it out or delete it or change it, what is the difference to the output caused by what inputs? And so that test the contract mentality really helps you to avoid digging into details and answer a question when you're like, I have no idea how to test this line. So again, this still can be challenging circumstances to come up. It happens every week for me. Something new that I got to figure out and testing, but I think testing the contract is a very helpful mental model and it'll help you there. So to continue to the learning, I would, in case you can't tell, I love talking about testing. I'm, especially in react and React Native world. So I would love to be in touch with you. You don't need to sign up for anything, but like if you go to my the workshop webpage here, there's ways to get in touch with me. Mastodon, LinkedIn, email address. If you ever want to reach out in the future. There's the test double newsletter. If you'd like to sign up there, I will still be in the conference discord, but if you'd like to join my personal discord for other react and react native testing people, we'd love to hear from you there. So those are ways for us to stay in touch. Again, just to share those links. I shared earlier in case you maybe care a bit more now because he didn't know me at first and we got started. This is a book that I've written about test driven development with react testing library. All those parts are very much the How do you have these end to end tests and components as working together. It adds Redux into the mix. Not because everybody uses Redux, but because the idea of a data layer separate from your UI can be helpful. So if you like my approach, if you're interested in, and try to get a TDD again, the intro of chapters are available for free. You can download those. It has a lot of theory about why TDD, and there's a $10 off link. This'll be available. I forget the timeframe, but it's at least a couple of weeks. So you can check this out. It's linked from the workshop webpage. You can get it there. ebook or paperback. Amazon won't let me put the paperback on sale, so that the paperback is full price. But yeah, if that's a helpful resource for you, please check it out. And I'm reachable. If you have questions about anything in there, or if I can enhance the book to make it clear. Test Double, we'd love for you to stay in touch via the newsletter. If you'd like, sign up for a test double.com slash newsletter. You'll hear things about react and JavaScript and node and go Lang and Ruby testing and software architecture and team stuff, agile stuff, going on there. Thank you all so much for coming. Thanks for signing up to give me a chance to speak. I love sharing these things. I hope something got you thinking whether you agree or whether you disagree, or whether you have better things for me to learn from to do better. I really appreciate the chance. I appreciate the conference organizers giving me the chance to give these workshops and to connect with you and share with you. And I hope we stay in touch online sooner or later. So thank you all so much for taking this time. Please feel free to give me feedback. If there's something that would have been more helpful to you, and maybe I can still give you a pointer asynchronously to get you to something. Have a good one.

Watch more workshops on topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?

There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.

Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
React Advanced Conference 2021React Advanced Conference 2021
27 min
(Easier) Interactive Data Visualization in React
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick & easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data & use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time & effort!