Introduction to React Native Testing Library

Bookmark

Are you satisfied with your test suites? If you said no, you’re not alone—most developers aren’t. And testing in React Native is harder than on most platforms. How can you write JavaScript tests when the JS and native code are so intertwined? And what in the world are you supposed to do about that persistent act() warning? Faced with these challenges, some teams are never able to make any progress testing their React Native app, and others end up with tests that don’t seem to help and only take extra time to maintain.


But it doesn’t have to be this way. React Native Testing Library (RNTL) is a great library for component testing, and with the right mental model you can use it to implement tests that are low-cost and high-value. In this three-hour workshop you’ll learn the tools, techniques, and principles you need to implement tests that will help you ship your React Native app with confidence. You’ll walk away with a clear vision for the goal of your component tests and with techniques that will help you address any obstacle that gets in the way of that goal.

you will know:

- The different kinds React Native 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 text, image, and native code elements to verify and interact with them

- The value of mocks and why they shouldn’t be avoided

- The challenges with asynchrony in RNTL tests and how to handle them

- Options for handling native functions and components in your JavaScript tests


Prerequisites:

- Familiarity with building applications with React Native

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

- You do not need any experience with React Native Testing Library

- Machine setup: Node 16.x or 18.x, Yarn, be able to successfully create and run a new Expo app following the instructions on https://docs.expo.dev/get-started/create-a-new-app/

131 min
28 Oct, 2022

Comments

Sign in or register to post your comment.

AI Generated Video Summary

This workshop on React Native Testing Library covers various topics including testing components, user interaction events, mocking, and testing API calls. The speaker emphasizes the importance of testing the contract of a component and provides practical examples and exercises. Just module mocking is recommended as a portable solution for mocking modules. The workshop encourages exploring different testing strategies and provides additional resources for further learning.

1. Introduction to React Native Testing Library

Short description:

This is an introduction to React Native Testing Library by Josh Justice. He shares the URL for the Git repo and invites the audience to work through the exercises. Josh introduces himself and mentions his previous talks and workshops on React Native.

Thank you. This is intro to React Native Testing Library, and I'm Josh Justice. I'm super excited. This is one of my favorite topics to talk about, is React Native Testing. And so we're going to dig in together on this today.

I shared the URL for the repo in the chat just now. It's in Zoom chat. It's in Discord chat. But if you go to this URL, rnte.st, rntest, slash London 22, you'll get the link to the Git repo, and this link will stay active in the recording in the future, so you'll be able to get this in the future. The Git repo of exercises you're going to be working through, or I invite you to work through, as well as other resources, the slides and the recording will be up afterwards.

Let me introduce myself. I saw a few names in the chat there that I recognized from yesterday's workshop on Expo. Hello again, glad you're back. I'm Josh Justice. I go by CodingItWrong on the internet. My personal home page is codingitwrong.com. Here are some of the things I've shared before on React Native. I spoke at React Native EU this year about test-driven development. I gave an extended workshop on React Native testing, a full-day version of this workshop at Chain React 2019. I'm giving an updated version of that workshop again in 2023.

2. Introduction to React Native Testing

Short description:

I gave an extended workshop on React Native testing at Chain React 2019 and will be giving an updated version in 2023. I also live stream on Twitch and maintain a website, reactnativetesting.io, where you can find information on testing components with React Native testing library. I work at Test Double, a consultancy that focuses on improving software and helping teams with testing and agile development practices.

I gave an extended workshop on React Native testing, a full-day version of this workshop at Chain React 2019. I'm giving an updated version of that workshop again in 2023. So if you're looking to go to Chain React, you can join us there. I also live stream on Twitch, and React Native testing has been my topic of choice recently. From my web page, you can get there. Stay in touch if you want to talk about things like this on a weekly basis or catch the recordings afterwards. So you can see a theme of being excited about talking about React Native. I also maintain a website, reactnativetesting.io, where there's a lot of information similar to what's in this workshop to get you equipped to test components with React Native testing library, end-to-end tests. We especially focus on the detox library for end-to-end testing on there, and I try to keep it up to date with all the latest APIs and changes and improvements in the React Native testing world. Because things are really changing and improving. I work at a consultancy called Test Double, I want to thank them for supporting me and giving me a chance to be here, giving you this workshop through the conference. So Test Double is a company that focuses on improving the world's software. And so if learning about testing gets you excited, if you feel like you can get something valuable out of this, we love to help teams with testing agile development practices, maintainable code, upgrading, focusing on end user needs, and so we would love to help you out, so check out test double on social media or test double.com if you think we can help you as a consultancy or custom corporate training or something like that.

3. Introduction to React Native Testing

Short description:

Today, we'll cover React Native Testing Library and Jest Native, which is a helper library for making assertions against React Native code. We won't cover Jest basics or end-to-end testing with Detox or Appium. The library we'll use is React Native Testing Library, which has a similar philosophy to React Testing Library. We'll focus on testing observable behavior and the component's contract with the rest of the application. Inputs include props, context data, and user interaction events, while outputs include the rendered UI and calls to function props. Let's dive in and start testing with React Native Testing Library, beginning with rendered UI.

So today what we'll cover in three hours is React Native Testing Library and Jest Native, which is a helper library for making assertions against React Native code. What we're not going to cover is Jest basics, I mean you'll see some of Jest testing basics as we go, but it's not mainly an introduction to Jest and also we're not going to cover end-to-end testing today with either Detox or Appium or anything like that, and I'll kind of explain the difference as we go along, but really it's specifically on React Native Testing Library. Afterwards, this URL, this is the same URL that's in the footer down here, so you can see it on any slide when the slides are up. You can go there from, I'll have links for more resources you can follow and also links to I have a Discord community with a bunch of folks that join on the livestream and talk about React Testing, React Native Testing. I'd love to field questions for you there afterwards if you have any more questions about testing. Anytime, please reach out. Okay, so we're going to start, that was the intro, Session One. This is Session Two, which is component tests focusing on rendering. So let's dive straight in and maybe about halfway through the three hours, we'll try to take a break. Maybe that will coincide with some exercise time as well, so people get dual time for that.

All right, let me make sure I see the chat on here. I'm going to keep, as we go, I'm going to keep an eye on the Zoom chat and on the Discord chat as much as I can. Actually, you know, I have my notes when we're in the code exercise. Those are going to be up on my phone. So anyone is welcome to come off mute to ask a question, anytime, but whenever I'm back on the slides, I'm going to pop back over to Discord. So I'll mainly watch Discord, but I'll try to keep the Zoom chat in front of me as well. Basically what I'm saying is, please interject and ask questions, because I'm happy to just help with whatever would be helpful to you.

All right. So let's talk about rendering and really the basics of getting started with component tests. So what is a component test? When you think about React or React Native, your application is organized into components, and in the component test, you are testing a component directly. You're not testing a whole application altogether. In the component itself, you actually write JSX to render out a component in the context of the test, independently of the rest of the application. So it lets you isolate the components. By default, all the children, like the child components of the component, are rendered out and included as well, but the rest of the application you're separated out from. And so you can focus on, does this component do its job with regard to the rest of the application? The library we're going to be using is called React Native testing library. The NPM package is under the At Testing Library organization, and if you've used React testing library on the web, it has a very similar philosophy. The code base is totally independent, because it emerged separately and because there's so many differences about the native environment, but they have exactly the same philosophy and are trying to converge on the APIs. Here's the URL for it, but also, these slides will be up later, but basically if you Google React Native testing library, you will get their docs. And from, when we do get to the exercises together, I have a link to my testing website with more information, and it in turn has links to the React Native testing library docs. So from there, you're going to be able to get to whatever you need in the exercises today or in the future.

So what do you test when you're testing a component? Some of these questions come to mind and especially kind of a few years ago before the testing tooling had evolved, you started to say, well, my component is a function and it's got other functions in it. Can I call them? Can I test a hook? Can I test a memoized property? Can I check state values that are set to see if they have the appropriate value that I would expect? And so these are the questions that people tend to ask. A common phrase is how do I test this line of code? And in some senses, like yes, you want the test to run through there, you want the test to make sure this line of code works. But the focus when it comes to writing a test, I have found that focusing on how do I test this line of code can tend to not be the most productive, it can leave you confused. How do I test the line of code? Instead, I would like to argue for a different way of thinking about tests. And none other than Dan of the React core team has a great quote that I think is very helpful in this regard. In a tweet in 2019, in the before times, he said, we don't encourage reading implementation details like state variables themselves. Instead, test observable behavior, i.e. what your component renders or does. Test from outside the component. And I think that's exactly right. That's exactly the kind of test philosophy that leads to robust, maintainable, helpful, useful tests. There's another way you can think about this concept, and it's called testing the contract. This term, maybe it comes from elsewhere, but I know of it from a book, called Testing View.js Applications. A different frontend framework, actually. Ed Yerber wrote the book, and he said, A component contract is the agreement between a component and the rest of the application. It's not something concrete in code, it's just the concept. It's, what is the concept of the agreement between a component and the rest of the application? He goes on. Other components can assume the component will fulfill its contractual agreement and produce the agreed output if it has provided the correct input. So that is what the rest of your application thinks about when it thinks about a single component, or what it is built in light of. It is assuming and relying on a given component doing what it guarantees, doing what it was built to do. That's what we can focus our tests on. If a component gets the right inputs, or for a given set of inputs, what are the outputs that the component produces? So I wanted to go to a question now, before I tell you what I think the inputs and outputs of a component are. I have four main ones that I want to talk through together. I'm sure I haven't thought of everything. I would love to hear from your perspective if you're willing to share in the chat. What are some of the kinds of inputs and outputs that the components have? There's no wrong answers. I just want to get your understanding. I'm pulling up the two different chats now so I can see your thoughts, but let's just take a minute or two to think about it. What are the inputs and outputs of components? I won't force you, but I encourage you to be bold, share your thoughts on what the inputs and outputs of the component are. In just a minute or so, we'll continue.

All right, Salvo says, input props and context data. Great, that totally makes sense. I wonder if you might have output queued up. So I'll give just a second in case you want to share some output. Salvo says, props input, output snapshot. Yeah, it makes sense. Salvo says, output rendered component on the screen. Makes sense? Cool, yeah. The way you all are thinking about this is spot on. So let's go and look at the way that I kind of structured it. You'll see a lot of familiarity. I kind of break it down to, as far as the things that I teach in this workshop, inputs are props. And in this workshop currently, I don't call out context separately. I guess it's a content. Values from context are not props, they're coming from a hook. So I think it could be helpful for me to call that out separately in the future. But yeah, data coming from a context, absolutely applies as well. Actually, you might not intuitively have thought of this, but I list user interaction events as inputs as well. The user is inputting a click, a tap here, or typing text there. And that results in, yeah, changes to the component. When the user does this, puts this in, something comes out. Outputs include the rendered UI and also calls to function props. So if a prop is past, sorry, if a function is passed in as a prop, or from a context or from a hook or something like that, if the component calls that function, that's a call, a method call, a function call going out into the rest of the application. And as we go through testing these, you'll see how we can assert that when certain input comes out, a function call comes up the other side, potentially with arguments that go along with it. Yeah, it occurs to me that actually web service return values should go in here as well. So we'll look at that as we go. So these couple of inputs and outputs are going to form the structure of what we focus on for most of the workshop together. So let's go ahead and start diving in and seeing how we can use React Native Testing Library to test inputs and outputs like this. First, we're going to start with rendered UI.

4. Testing the HelloWorld Component

Short description:

In this part, we start by discussing the outputs of a component and then move on to testing the HelloWorld component. We confirm that the text 'Hello World' is shown on the screen using the Render API and the screen.getByText method. We also provide a testing tip to ensure that tests fail when functionality is broken.

Kind of surprisingly, it's the outputs down at the bottom of my list, but that's the easiest thing to get because we need a component before we can do anything with it. So let's go to the code. Let me get in here. We got this sandbox. We're going to test. We're going to start the server. Excuse me for just a second while I get the right windows up here. Yeah, and we're going to lecture one. So this part is not what's in your code. This is unique to me, but you're going to be doing an exercise with similar types of things as we're going to be doing together here in the workshop. So in the, you know, the lecture examples, basically.

Alright, so let's get, we got some very trivial components here, but let me just get them up in a simulators so you can visualize them. That's taking a little long to open. So maybe we'll come back and visualize it. I bet when you see the component, you'll be able to follow along. So this is a HelloWorld component. This is an extremely simple component here. We do have an image and an SVG we're going to get to in just a moment, but for the moment focus on the HelloWorld message here. Evgenia asked about GetLink. Yeah, let me give that to you. There you go. There's the link, Evgenia. So you can pull down and we're not to the exercise yet. This is just what I'm demoing, but in a few minutes, we're going to start exercise one, and you'll be able to pull that down. You're welcome. Alright, so in lecture one, we have, I love this picture. I found it a couple weeks ago and then forgot about it. It's so great. And Walter Bruner on Splash, good job with this picture of a waving squirrel. Love it. So yes, so this is a component that's just rendering out some simple stuff. Some simple text, a PNG or a JPEG image, and an SVG graphic. And so this is output. The component is outputting these things, and we want to test them. It's unconditionally rendering it, so I don't know if it's the most valuable to test. But just as an example to show, how can you confirm that a component is showing a given thing on a screen? And so we're going to start with the text.

Oh, yeah, and I got to get my notes up here. One second. Yes. So in the component, you can see it's pretty straightforward. If you've used React Native before, and if you haven't used React Native before, you're going to get a taste as we go. But in React Native, we use view components and text components, so we have some text showing Hello World. Very straightforward. So how can we confirm that that text is shown in here? Well, the API to render a component in React Native, a testing library, is Render. Looks like my language server protocol is not importing it, so I'll need to do it myself. We import Render from testing library React Native. And then we render out the Hello component. This was JSX, just like we do in the rest of the application. Let me save this and run it just to see the test output. Lecture sandbox. Hello. Yeah, this needs to be in a test. Sorry, I'm not thinking. So in it, you need an It or a, you can also use the keyword test, but Test or It, you say here, and you say, renders a Hello message. Just a nice description in your spoken, natural language of choice of what you're testing here. We're testing that it renders a Hello message. And so now when the test runs, it passes. Now just by rendering the component here, all that we're confirming is that it doesn't blow, it exists, and it doesn't blow up or have some kind of error as a part of the render. But we wanna test more than that. We wanna test that the text is actually shown. And so we can do that with expect. So expect is a JEST API for you to check that something is the case. So you wrap that in expect, you say screen. That's not gonna auto import either for some reason. We bring in the screen API from testing library react native. And we can say screen.getByText. And then we can actually type in the text that shows. Let me check the capitalization. It's a capital W for world. So we can say expect screen.getByText. And what this will do is this will give you an element on the screen that contains that text. And you can say toBeVisible. So this will check that it exists and also does some checking around visibility. It hasn't been set to display none or zero opacity or shrunk down or something like that. So it does its best to confirm, hey, yeah, a user should be able to see that text. So let's save and see. So the test passes. So one testing tip that I would give is when you have some code written and you add a test to make sure it works and it passes right away, don't trust it. Be worried. You want to make sure that the test will actually fail if something is broken. And so when you get it to green, go and break your functionality to make sure it fails afterwards. So what I'm gonna do is temporarily go in and just delete this text and save my component. Just automatically reruns. And now we can see the error. Unable to find an element with text, hello world. So now we've confirmed that this is not a false positive. This test will really fail if the message is not there. And then when we put it back and save, we get green. So this test is really confirming the functionality. It's really confirming that that content is present.

5. Notes on React Native Testing Library

Short description:

This section covers some important notes about React Native Testing Library. It explains that the library is case-sensitive and matches full strings by default. It also introduces the use of regular expressions to match text more flexibly.

So a few notes about this. I forget if this is case sensitive. So let's try to find out. I'm gonna make lowercase w. Yes, okay. So it looks like it is actually case sensitive. Okay, so that's good to know. Another thing I was gonna say is it matches full strings by default. So a full text element. So if you just include part of the string, it's gonna say I'm unable to find the element. But you can get around that by using regular expressions. So if you want to say, I want to match some text that contains hello somewhere in it, you can use a JavaScript regular expression by using slashes. And we're not gonna get into regular expressions too much, but you can Google those or let me know and I can send you a link with reference, but those let you match more flexibly. So we see that when we put in a regular expression, it says, yep, I found text that matches that regular expression. In this case, the substring, hello.

6. Testing Accessibility and Matching Elements

Short description:

To make components more accessible, React Native Testing Library recommends writing tests that ensure testable components are also accessible components. By using screen readers, we can verify that visually impaired users can interact with the app. Adding an accessibility label to images and SVGs allows us to test their presence. Getter functions like getByLabelText and getByPlaceholderText help find elements in React Native Testing Library. These queries prioritize accessibility and reflect the experience of both visual and assisted technology users. The getByRole query allows specifying the accessibility role, providing more information to screen readers.

Now what about matching things that aren't text, confirming that the image shows the photo or confirming that this SVG shows? So it turns out that if you write your test in a way that React Native Testing Library recommends, a testable component is also gonna be an accessible component which is to say it's a component that's usable by people with different abilities. In particular on computers and phones, visual impairment is something that there's a lot of facilities for. I mean, you may have heard the concept of screen readers or maybe some of you have actually used them for development, I hope so. We're gearing up to get into accessibility focus in the project that I'm on right now in my consulting job and so we're gonna be using screen readers to test that when somebody's visually impaired or blind and they're using a screen reader that will actually verbally read out the contents of the screen, we test to make sure that the app is actually usable and interactable. If you haven't learned about accessibility like that, I would highly encourage you to dig in. I should actually add a link to that to the web page because I love pointing people to this because I only learned about accessibility later in my career and I wish I'd made a focus on learning it sooner because it lets you build apps that are usable by more people. So but the point of all that is, if you set up this image and this SVG so that it reads a description of it out loud to a screen reader, that's also testable and so, you know, testing, automated testing and accessibility can work hand in hand. So to get the image of the squirrel to be testable, let me get this zoom thing out of the way, and what we need to do is add an accessibility label to it and the accessibility label, we can just say squirrel waving. So now there's some text attached to that image that can be used for accessibility and then we can test for it here. Let's say it displays the squirrel, let's use the words that are the hardest to spell in a workshop. So when I render hello I expect, Avgainia says that they are at the point that I mentioned where I knew nothing about accessibility, so yes, I will share that link about accessibility afterwards. There's a conference talking about React Native Accessibility in particular that was really good and actually there was one at React Advanced this year and so I will get the name of that so that y'all attended the conference so you'll be able to watch that video and there's also one from a few years ago at a conference. So I'll share those. But here's your early taste of it. So when we have an accessibility label on the element, what we can say is expect screen, getByLabelText. That is to say accessibility label. Let me see if my language server will tell us about it. It looks like not so much. I guess my linter is getting in the way here, but so let's just fill this in. So we say, get an element by the accessibility label text squirrel waving, and then expect it to be visible. We're gonna save, it passes. Again, we talked a moment ago about how we don't trust it. That's not good enough for us, we need more confidence. So let's actually break this again. We'll remove that accessibility label saved, and we see that the test fails. So that confirms that we real, it's passing and it is really confirming that element with that accessibility label is there. It doesn't confirm which JPEG is being shown. I'm not actually sure. I haven't checked to see if you can confirm which images being shown there, so that could be something to look into. But really here, if the point is like, oh, sometimes I show the squirrel image and sometimes I don't, you can check that the image with this accessibility label is present. For an SVG, it works the same way. If you're specifically, if you're using the package called React Native SVG Transformer, this is very common, and I think every time I've seen SVGs included at build time in a React Native app, this is the way it's done. So when you're using this package, you basically can import directly from an SVG file directly in your components, and you get a component that you can use as a React component. So here's the waving hand, and what we want to do is exactly the same. You want to give it an accessibility label and call it waving hand. Actually this time, you know, let's do this in test-driven development, we'll do this in the opposite order. So in test-driven development, that is not the focus of this workshop, but in that approach, you follow a process called red-green refactor, where first red, you write the test for the new functionality you want, and you watch it fail. And then, once that test is failing, then you do green, you implement the functionality to make that test pass. And one of the benefits of test-driven development is you've actually confirmed that it is really testing something because you have seen the red. You don't need to remember to go back to the red because you've seen it red from the start. It's actually fewer steps if you're testing to make sure your tests really fail in the appropriate way. So let's do this test-driven by writing the test first. We're gonna say it displays a waving hand icon. We're gonna render hello, and we're gonna expect screen get by label text, waving hand to be visible. So now we're gonna save, and I expect this test to fail because we haven't added that accessibility label yet. I save and it fails, unable to find the element with accessibility label waving hand. So that's the red step. Now we can go to the green step by implementing this, and it's very easy to implement. We just add an accessibility label, and with language server, you get auto-complete there, so that's nice, waving hand. We save and the test passes. Now we're confirming the presence of an SVG as well. All right, let's go back to the slides and talk about what we just saw in the code. So when you're trying to find an element with React Native Testing Library, there are a series of getter functions. They're available on the screen API, and you call get by text, or get by label text, or get by placeholder. I'm gonna show you an example of what that means. Get by text will let you find a text element, which in React Native, I think always, always or almost always is in a text element, a text component. Even when you have some text that's being displayed by a third party library, under the hood, utterly, there's a text element there. So if you see text on a screen, get by text should be able to get it for you. If you have a text input, this is a very common case, of course, because you're typing into it. Placeholder text is a great way to pull it up. If you have a placeholder in that text input, you can get by placeholder text. And that is the case, whether that's findable by that. Whether the placeholder is visible, or if you've typed into it, and so the placeholders now hidden, you can still get the input by that placeholder text. As we saw, if you have an image, using the React Native image component with some kind of JPEG or ping or something like that, you can add an accessibility label prop. And then you can call screen.getByLabelText. And with an SVG is the same way, as we saw, accessibility label, and get it by label text. And then your components are now more accessible to screen readers as well as added. So which query should I use? There are actually other options as well. And you might even be choosing between the ones that we listed here. So there's a page. And again, the slides will be up. So you'll be able to get back to these. A page on callstacks website for React Native testing library that says, How should I query? Which should I use? And they have a quote, this philosophy comes straight out of React testing library and the rest of the testing library ecosystem. Your tests should resemble how users interact with your code, component or page, as much as possible. And the implications of that are here. When they list the queries in order, they start at the top and they say the highest priority are queries accessible to everyone. That is queries that reflect the experience of visual users, as well as those that use assisted technology. So basically these queries are confirming if you use them, it's something that both people will be able to see with their eyes, or they'll be able to hear through a screen reader or another technology like that. So get by text does that because text is on the screen. There's get by display value, which shows you the text entered into a text input, placeholder text and get by label text, which allows you to query every element that is exposed in the accessibility tree. So a lot of good options there. There's other options further down on the list. One important note and some of the folks who helped me out on my livestream and in my discord community have pointed me to this. There's a screen.get by role as well. And this is something that allows you to specify the accessibility role. The accessibility role gives more information to screen readers as you go through that lets them know, hey, this is a button, this is a text input, this is a header or a navigation menu or things like that. And so that can be a really clear way. You can see here as we read this, it just kind of makes sense. Get the button that is named save changes. That can be helpful if you have maybe save changes as the title of a modal as well.

7. Implicit Roles and getByRole

Short description:

React Native Testing Library is discussing making getByRole the recommended way to query, similar to React Testing Library on the web. There is an issue open regarding implicit roles, where pressable elements are not recognized as buttons by default. The library is working towards adding support for implicit roles and recommends keeping an eye on the documentation for updates.

So this really makes it clear. Give me the button that has the namesave changes. So there is an issue open on React Native Testing Library's repo where they're discussing making get by role the recommended way to query. And that's the way it is on React Testing Library on the web. But there's at least one feature needed first. They've fixed, a contributor has fixed one of the issues needed, but there's one remaining is implicit roles. This is basically the idea that when you have a pressable or some other touchable input, touchable opacity, all those weird names for button type things in React Native. If you just use a pressable, that should be considered a button by default, but it's not currently. You have to explicitly say in your code. I forget the prompt accessibility role equals button. And so that actually is not the most realistic. It does work as a button. It's fine for accessibility, but React Native Testing Library is not yet recognizing it as such. So there's some hoops you have to go through to get getByRole working, but they're hoping to have someone to contribute these implicit roles soon. And at that point, getByRole will be the top recommendation. And as a result of using it, you'll need to learn a bit about accessibility on the platform. And I did that on a stream recently. I was learning some about it. So I'm not recommending yet this as the top because it's not yet officially recommended in the docs, but keep an eye on the React Native Testing Library docs or check me out on social media. I'll be posting about these things in the future. And this might be coming soon, but that's why this is not the main thing I teach in the course as of right now.

8. Confirming Element Presence and Testing Props

Short description:

To confirm the presence of elements, use the toBeVisible matcher provided by Just Native. It checks if the element is present and visible. Another way is to use screen.getBy or screen.queryBy, which return a clear error message or null, respectively. Confirming absence can be done by using expect screen.queryBy, which returns null if the element is not found. The screen API is a shorthand for render result and provides autocomplete benefits. It is recommended to use screen.getBy for confirming element presence. The screen API was added in the last versions of React Native testing library. There is no difference between using screen or render result, but screen offers better consistency and autocomplete support. When testing props, modify the component to include the prop being tested. By default, the component should greet 'world' if no name is provided. Test the case where the prop is provided and ensure that the component greets the provided name.

All right, so we have the ways to get elements on the screen, screen, getBy, whatever. So how do you confirm the presence of it? Well, as of the latest minor release of Just Native, which is 5.1, Just Native provides these matchers, which is to say the things that you add on the end of expect. And they've recently added toBeVisible. And so this is now the recommended way, at least my recommended way, I think it is the recommended way to confirm the presence of elements. And the reason why is when you call getBy and then toBeVisible, it's gonna confirm that it's really present there. If the element is not there at all, you're gonna get a nice clear test failure message. And the visibility checks as well to make sure it's not displaying none or height zero or opacity off or anything like that.

There are a few alternative ways to confirm the presence of elements, it was just a couple of weeks ago when toBeVisible was added. The way to do it was screen.getBy expected to be truthy. Which is to say, is it a real element not null? Another way to do it was expect screen.queryBy to be truthy. So this is kind of complicated. I should have made a clearer slide, sorry about this. getBy if it fails, if you can't find the only you're looking for it will error out immediately. QueryBy instead will return a null and so then if queryBy does not find the element you'll get a null. Jest will say expect null to be truthy and Jest will tell you, oh, I expected truthy but actually, it's null instead and that's all the information you get. You have to look at the code to see what it was looking for. By contrast getBy gives you a much clearer error message that says, here's the element that I was looking for that I didn't find. Now actually, because getBy errors out on its own you can just say screen.getBy and admit the expect altogether but in the Testing Library family it's recommended that you do wrap it in an expect because that makes it clearer that you're intending to check the element. So it was kind of weird before like do I wrap screen.getBy in expect? I don't really need to but it's kind of recommended and maybe more helpful but it is a lot clearer now because now we have to be visible and so now we have a very specific reason that we wanna wrap it in an expect to get those extra visibility checks. So I would really recommend this if you're confirming something is present, expect screen.getBy to be visible. Confirming absence is somewhat different and there's really only one main way to do that. You do wanna use expect screen.queryBy in this case because queryBy returns a null and so in that case it won't blow up. You can't check that the getBy returns a null because it'll just blow up, but if you say, oh, I wanna make sure that the queryBy here is null, that is to say it's not found and then if it is something, if there is an element present, an element will be returned and the test will fail because it will say I expected it to be null, but it was not, I got an element back. So that's the way to confirm that something is not present. So that's rendered UI, that's kinda the basics of confirming what you see rendered out in the component.

We are briefly gonna go to props just because props is pretty simple and just a slight tweak here and what we check. How can you check, how can you account for props in your tests? So let's do that quick and then we're gonna go to our first exercise. To the code, and in the meantime of going to the code, I'm just gonna check and see has chatted anything else. Okay, still has a questions. My project is using an older version of the testing library and query stuff on the return from render method. I'm not familiar with the screen. So I was wondering if there's a difference between the two. That's a great question, Stella. And I should have pointed out the screen was newer. Yeah, so it was just the last couple of versions of React Native testing library that the screen API was added. And there's not a difference. The difference is just, instead of, well, let me get it up on in the docs in front of you so that the others on the call and on the workshop can see this and see the two options. But the short version is that either way works just fine. So, when you have the render function there's a render result that is returned. Yeah, they might not show examples anymore. But basically, yeah, so latest render result is kept in screen variable that can be imported from testing library, React Native. So previously, let me just show in the code here, previously until a few minor versions of React Native testing library before, what you would do is, instead of screen, you'd say const getByText equals renderHello. And then you'd say expect getByText to be visible. Let's just make sure this passes. It does. Yeah. So basically, the render result, it would return these values that would have all the getters and the query functions on it. And you would just de-structure it typically to be able to get the different methods you use. Now I'm getting a linter warning here, and it says avoid destructuring render results. Use screen.getByText instead. So basically, screen was added as a convenience following the approach where it was available on the web side. One of the nice things about it is, instead of having to change the import statements, query by label text. Like this, in my applications, I was doing this all the time. Oh, I got a query or getBy something else, so I need to add another destructuring here, and it just kind of got tedious to maintain this line, and also passing it around if you have separate parts of your test can be hard. But when you just have the screen API, you just say screen.getByText, and you're good to go. If you're, it's better for language server as well. Whether you're using JavaScript or TypeScript, autocomplete works a little bit better. Let's see if it's working. I think there's something wrong with my language server right now, but yeah, you can basically just type in screen. It'll import it from the package, and then you can type in getByText. It autocompletes and gives you all the options there. So that was a long way to say, no, there's no difference. Screen is just a shorthand API. You don't need to migrate your code over, but it's recommended going forward. Oh yeah, Costella has used const utils equals render, then use utils like screen. Right, so that would actually have the benefit of the autocomplete as well. Let me put that on the screen here. Let me save that to make sure that works. Yeah, so that works as well. That probably has a lot of the autocomplete benefits. Now, the cases where you're having to pass in it, if you have helper functions that set up your rendering or before each or something. Well, before each is not recommended in there, but this utils, you might need to pass it around, whereas screen is a standard API. It always has the same name. So just slight consistency across the ecosystem there. But yes, cool. Great questions, Costella, thank you. All right, and we're we coming back? I think we were coming back from the code. Nope, no, we had not gone to the code yet. Let's go to the code. We got to talk about props. Okay, so props. Nope, this is the wrong thing. One second, and we'll have it in here. All right, so let's change our hello component to take a name. We wanna greet more personally, and we wanna say we wanna greet someone by name, but by default, we greet world if there's no name provided. So we're gonna make name interpolated here. So I'm gonna say this change. All of my existing tests actually pass because, when I render the component without a name provided, world is still the default, but now let's test the case where this prop is actually provided. We're gonna say it's... Greets the name provided.

9. Testing the Contract and Exercise One

Short description:

In this part, we discuss testing the contract of a component, specifically how props are tested. We arrange a scenario and observe the output to verify the component's behavior. We then introduce exercise one, which involves adding tests for the movie row component in a simple movie list application. The exercise provides instructions on obtaining an API key and adding tests to the movie row spec. The movie row component renders a view based on a movie prop, and additional functionality is added if the movie was added today. The goal is to fully specify the component's behavior by writing tests and modifying the component as necessary.

Good name, render. Hello. I'll greet Josh. Let's get an expect in here. I will say hello, Josh. I save and it passes. Once again, let's not believe it, let's not trust it. So let's take this out and make sure we can see the test fail. It does. And now we save again and it's passing. So now we're confirming that in the case where certain props are provided, certain output happens. This input results in this output. You can also check if you wanted to, you can check that hello world is not visible. There's a lot more you can do depending on how much confidence, how much precision you want in a given test.

So, and I wanted to say something about the concept, thinking about the idea of testing the contract. You could say, how do we test this prop? I wanna test this prop, how can I test that? So that mental model, what does that mean? Like a prop itself doesn't do anything. A prop is just passed into the component. So if you want to test that it will accept the prop, you could just type in the prop and see that nothing blows up. I suppose it's possible to write a component that will blow up, or maybe if you're using TypeScript, you'd get a TypeScript error and you know, like, oh yeah, no, not supposed to have that prop. But really, why does the component have the prop? What is the contract the component is searched for the rest of the application? What does it guarantee to do as an output if a given prop is passed as an input? And then you test the contract on that. So really the presence of something like a name is really kind of the scenario or situation. And so we write the test for that. We arrange that scenario and we see what happens as an output from it. So that's the something that we're testing coming up. And that's kind of, I mean, you know, a lot of ways to use props, but that's sort of the starting points as far as how to test props. And so we're good props as an input and rendered UI as an output, we have covered at a basic level.

So with that, I'd like to go to exercise one and send you over there. I'm going to show you the readme real quick, but this is an intro to RNTL exercises. That's the, the Get Repo. And it's under the exercises folder. One exercise one, which is written funny because I just wanted to sort in numeric order. I guess I could just exercise whatever I'm weird. So one exercise one Markdown file is where the exercise is described. And as this file says, if you want help, you can go to this page on react-native-testing.io. It covers the same material that we've talked through in this first segment. So let me show you that and get you familiar with the exercise. So we want to, I'm gonna start it up and boot it up in Expo here. You don't need to run the app in a simulator or emulator, but you absolutely can if you want to visualize it. So our exercises, we're going to be gradually adding more and more tests, a couple of different tests, to a very simple application. And this is the application. It is a movie list. And so vertigo and the sound of music show up on there. Oh, there is a Read Me instruction though. So let me do this in order. So in the exercises repo, you'll see the Read Me. We'll go through here. It's got the basic requirements for doing React Native development. It says please install the dependencies. And so then here's another step. Get an API key if you haven't done this before. So just so that each of you has your own set of movies and nobody's sharing inappropriate movies for a list that we can all see. We're going to have separate API keys. This is totally free and there's no personal information collected from you. All you do is go to api.outsidein.dev and click on create an API key. This is used for a book of mine that I self published on testing, but it also works for this. So just click create an API key and it will show, I will show it here and then I will hide this quickly so we don't all use the same API key. If you just click on that you'll get a unique key and then you'll be able to paste it in to SourceApi.js. You can just paste it in there. Again, don't use my key, please use your own. So this will allow you to have your own list of movies. So you'll get Vertigo and the sound of music showing up by default. And then you can add in another movie as well. So you can say Rango, a classic. It'll get auto corrected apparently. It's the name of the movie, right? Rango? Click on save. There's a bit of a delay intentionally so that you can, to make sure your test will handle delays, but Rango will appear and it will actually show this little indicator indicating this is a new movie that you've added today into your list. So that is the extraordinarily basic functionality of this app and that's where we're gonna be adding tests to. Yeah, so I will be available in the chat once the exercise starts in just a minute. So if anybody's having trouble getting this running, please let me know. I'd be happy to help out with that, but now let's go to the exercises. Exercise one says, we're gonna add tests for the movie row component. This component is already written and functioning and there's an empty test file for it. And so your mission, if you choose to accept it is to add all the tests to movie row spec that you need to fully specify the component's behavior and feel free to modify the component as necessary to help you write better tests for it. The movie row, again I really have kept this just so extremely simple to not clutter it up. Movie row takes in a movie and it renders out a view. There's some styling going on to get my beautiful look and feel over here. But the view shows up, there's text that's shown, and then if movie is added today, that's just a property that comes in from the server, then this new SVG is rendered out and you get the orange SVG on the right-hand side. Yeah. So that's your mission is to write a test. The test is just empty. It says, it.todo. Maybe I should've, you do need to delete that to make sure that it actually runs. So when you run these tests, when you run the test, it's gonna say todo exercise one. So if you delete the todo, you add an error function there. Then it runs. This test passes even though nothing has happened in there. So if you want to actually test something, but yeah, and you can rename it as well. You can have multiple tests. So yeah, that is the exercise. And there's some notes here about designing your tests.

10. Testing the Movie Row Component

Short description:

Here's the link to the page on React Native Testing IOS component testing. Let's get started on testing the movie row component. Try different options and query methods. I'll show you how I wrote the test for this component and encourage you to share your approaches. Testing content and behavior is recommended with React Native Testing Library. For testing appearance, I recommend using Storybook. Snapshot tests are another option for confirming JSON output. However, they can be large and brittle.

Just some suggestions to remind you of things we talked about. And then here's the link. Feel free to ask questions in the workshop, Discord chat. Zoom chat is fine as well, but Discord chat is going to be more permanent for viewers later. And then here's the link to the page on React Native Testing IOS component testing. You can pull up that page and then there's similar material to what we went through just now. So yes, let's get started on that. Let's shoot for 10 minutes. Maybe you'll end up getting a break there. Maybe most folks will be done by that time. But in case anybody has a challenge in getting set up this will give everybody time but work on testing the movie row component. If you finish you can try different options. Try different, get query methods. You can try the React Native Testing Library docs to try different methods. But yes, so go ahead with exercise one. It's 3.50 right now. So at four o'clock in 10 minutes we'll reconvene and take a look at what you got. And in the meantime, I'm gonna be keeping an eye on the chat to see if I can help. So yeah, enjoy the exercise. All right, let's reconvene. I think what I'd like to do here is I'll show you how I wrote the test for this component. And I would love as I do that for you to be thinking about how yours may have differed. Something that I wanna emphasize is that I really tried to just be open-handed to different approaches. There are different ways to approach testing with different trade-offs. And so my way of testing this is not necessarily the right way. You may have ways that make more sense to you personally or to your team or to your context. And so that's totally valid as well. So I just wanna encourage anyone, if you'd be willing to share in the chat afterwards how you tested things differently, maybe. This is a pretty simple case. So maybe we all tested very similarly, but if you chose something different or if you tried something different as an experiment, I'd love to hear about it. And I will acknowledge it as a totally valid alternate option. I'm gonna get talk about pros and cons, trade-offs, costs and benefits. So here's the solution that I did and I'm gonna run the test. So for the movie row, first I had a test that said Display is the name. So I set up a movie with the title Spiderman, No Way Home. And I noticed here that I set up an object that only has that one property. I don't know and actually you maybe don't know from just looking at that class what the other properties of this object are, but for the sake of this test, all the movie row cares about is title and added today. And actually it won't error out if the added today prop is absent. And so I just passed in the minimal for this test scenario. I rendered out the movie row component with that movie. And then I checked to see, I checked for the text for the movie title. And I decided to look that I'm using the variable instead of duplicating the string down there. But I confirmed that the text of the movie title was visible on the screen. After that, I had two different tests for the new icon. If you're remembering here, there's this added today icon that sometimes shows and sometimes doesn't, depending on the case of added today. So that's two different scenarios. And so I had added today true and added today false and passed those in. And I separated out these three different tests. We'll talk more about that in just a second. So when added today was true, I said screen get by label text added today. And this is the important note. I added the accessibility label to this SVG so that it could be queried in that way. And then in here, I said get by label text added today, expect it to be visible. And then down here where it does not render, I did query by label text, and I expected it to be null. I expected it to be absent. Something I'll say about the separation here, you could combine two of these, you know, these two, like, you know, in the situation where it was added today, make sure that the name is there and the added today icon is also there. And then in the false case, that kind of, well, you could maybe do two renders in one test, but it's probably a good idea to separate those out in the tests, but in this one, you could assert on the title again as well if you like, like, let me assert on everything about this scenario when added today is false. And that can be an approach. But I kind of like to really keep my individual tests small and focused around one behavior of the component. And so like the fact that it always displays the name and the fact that it sometimes displays a new icon and the fact that it sometimes does not display a new icon are three different behaviors, I would say, or yeah, behaviors slash scenarios. So I find a lot of benefit of splitting up my tests in those ways. But yeah, if anybody would be willing to share, how did you write your tests in any kind of a different way? Was it something you chose to do differently or just happened to do differently? Cause it's just what came to mind for you. I would love to throw it to you if you have any input on differences that you might've had in your solutions. Thanks. All right, let's continue on in the slides. So a few more notes about testing, rendering and props. Number one, the test that we've been writing here have been focused on testing content, not appearance. So we haven't asserted like, you know, which SVG shows or which color it has or whether it appears to the right of the other thing. You can try to assert on that by asserting on like style props, but that's hard to do. And I have found, in my experience that that tends to not provide a lot of value. It doesn't actually confirm that things look the way you expect. It just confirms that you put the style prop on that you expect it to. It's kind of like double checking at that point which doesn't add a lot of value. Instead, to confirm the components look the way you expect in a very effective way, I recommend Storybook. So Storybook is a really popular tool for presenting components in different states, different UI states. And they had a presentation, I believe, at React Advanced talking about ways to do that or someone using Storybook. I forget if they work for Storybook or not. So Storybook is a great tool for testing appearance. Using React Native Testing Library, I really recommend that for testing content and testing behavior or functionality. Another option is snapshot tests. Those were raised earlier in the chat. So in a snapshot test, this snippet may be out of date, but basically you do use React test renderer, you pass it some JSX, you convert it to JSON, and then you say, expect tree to match snapshot. This is basically confirming that the JSON outputted by the component has not changed since before. So that is output of the component. And that checks all the details of the rendered JSX. So you can make changes inside the component to reorganize where logic is and things like that. But as long as the rendered JSX that comes out is the same, this test will still pass. And if it changes, you can review it and see if that was intentional or not. I tend to agree when it comes to snapshot tests with Ken C. Dodds has written about them in years past and said that he feels like it, they tend to be very large and kind of brittle and it's very tempting to not review the changes.

11. Testing User Interaction Events

Short description:

Ken C. Dodds recommends focusing on specific parts of the rendered JSON and asserting on individual parts that you care about. Snapshots are not necessary when focusing on checking texts or element presence. Snapshot tests have limited value, but can be useful for checking specific outputs like generated readme files. In this workshop, we'll equip you with behavioral testing tools for your tests. Now let's move on to the next topic: actions and mocks. We'll start by looking at testing user interaction events with a straightforward form component. The form component includes a text input and a send button. When the button is clicked, the onSend prop is called and the input text is cleared. We can test this behavior using the React Native Testing Library and the FireEvent API. By simulating user interaction events, we can verify that the message field is cleared after clicking the send button. To do this, we render the component, type in some text, simulate a button press, and then check that the message field is empty. If the test fails, we can debug the failure output to identify the issue and make necessary adjustments. The jest native library provides the toHaveProp matcher, which allows us to confirm the presence of a prop with a specific value.

And it tends to couple you to every single aspect of the output of the component, which is not what you tend to care about. Ken C. Dodds would recommend like, oh, you know, don't assert on all of the rendered JSON, like focusing on a specific part of it and just to assert on individual parts you care about. And if you focus in all the way, you get to the point where you're not focusing on JSX at all, you're just checking some texts or checking an elements presence. And at that point, snapshots are not needed. One important note if you haven't used snapshots, snapshot here does not mean an image of visuals. It means a saved state of the JSX data structure. So this actually is not confirming the look and feel either, there's no image to review. So I've not found a ton of value in snapshot tests because of that. I have occasionally, there is a code generation project that I did where I wanted to check the readme that was outputted by it. And so I use snapshots to record the state of the readme because that was simpler than writing out the whole readme. I could confirm that it looked the way I expected it to. But generally I don't reach for it. If it works for you, great. But I'm hoping in this workshop we'll equip you with some behavioral testing tools you can use and maybe you will prefer those for your tests as I do for mine. Let me get the Zoom shot back up on here. Yes, okay. So now we're moving on to our next topic, actions and mocks. So in test into contracts, So far we've looked at props for input and rendered UI for output. Next, let's look at user interaction events. This is not so intuitive as an input, but I would assert that this is a way that data gets into your component and causes changes to happen. So let's look at testing user interaction events. I'm gonna switch back to my lecture sandbox here. We're gonna go to lecture two. All right, let's jump in. Oh, and I had to pull up my notes on here. All right, so here, new message form, we have a straightforward form component. Let me show it to you in the app so you can visualize it. So for lecture two, we have a straightforward form component. I haven't even styled it, but you can type in some text, and then you can click send, and it clears up the message as though it's a message that's been sent via text messaging or a chat app or something like that. So it's about as simple as components you can expect. We've got a View with a Text Input, and a Pressable we can press to click Send. And when you click that, if there is an on send prop passed in for that kind of an event handler, it is called with the input text. And then either way, we clear out the input text. We send it to the empty string afterwards so that it's cleared out and ready for users. So how can we test this? So thinking about the outputs, what are the effects of interacting with this form? Well, typing into it doesn't make too much of a difference. That's typing into a text input. We probably can assume that that works. But the real action happens when we click on the Send button. And this handles Send happens. And there's actually two behaviors, two outputs, you could say, or effects of clicking on handle Send. One is the on Send prop is called, as a function. And two is the input text is cleared. So let's handle clearing of the input text first, because that's going to be a simpler step forward as far as new testing APIs. So we want to say describe new message form. And I like to, this kind of feels like one scenario under here, so I like to add another describe block. I might say pressing Send. And then I'm going to say it clears the message field. Now, Render, new message form. It doesn't need any props. It's pretty straightforward. And we're going to use, there we go, there's my language server. So we import Render, and then we're going to call FireEvent. If you use React Testing Library on the web, you may be wondering about FireEvent. We'll talk about that in a moment. FireEvent change text, that's the name of the event in React Native. And we're going to get the element first. We're going to do screen.getByPlaceholderText message. And then we're going to provide the text that we want to type in. So let's start with, hello world. So we can save this. The test will run. And so this is going to render the component and then type in that text. And so instead of getByText, we're using getByPlaceholderText to get an input by its placeholder message. If you use the React Testing Library on the web, you may be familiar with the User Events Library, which is now the recommendation for interacting, especially with React in particular, and User Event is really great. You absolutely need to be using User Event on the web instead of Fire Event whenever possible. User Event is not available on React Native. And so in React Native, Fire Event is what we have, so that's what we use. If you would be interested in implementing User Event for React Native Testing Library, I'm sure maintainers would love you to contribute, but Fire Event is what we use in our NTL. So we change the text there. Now we need to press the button and that's very similar. We say fireevent.click, oh, sorry,.press. It's a press on React Native, and then we get the element. Screen, in this case, we can do get by text again, send, the send button. Now we need to check the result. So how can we confirm that the message field was clear? Well, we can do it like this. Expect screen get by placeholderText, message again, to haveProp, value of empty string. We save and it passes. You may remember by now, we don't trust it when it passes right away, we want to break it first. And the way we can break it is to break the behavior. We're testing that it clears the message field. So let's change the components so it doesn't clear the message field. We save, it reruns, and now the test fails. Another thing about making sure you see the test fail is you can see the failure output to see if it's failing for the reason you expect and to see if you get good, helpful error messages. If not, you may be able to tweak the test to get better failure output. So the test said, we expected the element to haveProp value equals empty string, but instead it had value equals hello world. So the text was not cleared. When we enable the functionality again and save, the test passes. So to haveProp is provided by the jest native library, which provides some of these measures to be visible. And so it confirms that this component has a prop with the given name, the given value.

12. Testing User Interactions and Function Props

Short description:

We test the value prop of a text input using toHaveProp. We use fireEvent methods to simulate user interactions such as changing text and pressing buttons. Jest mock functions are used to test the output of calling a function prop. We can check if the function was called and with what arguments using toHaveBeenCalledWith. Mock functions provide a way to inspect the call history of a function. They are helpful for testing the output of a function call.

Now, if you've been in testing circles for a while, you may ask yourself like, isn't that prop an implementation detail? Aren't we testing implementation details by testing this? And I would argue that in this case, it is implemented as a prop, but what we're testing is what something that's absolutely user visible. What is the text in the test input? That is our goal. Our goal is to focus on something that the user can see. We're confirming a user behavior. And how do we get the value that's shown to the user in the text input? The value prop is the way we can get that. So even though we're accessing a prop here, I wouldn't recommend testing every single prop and every single child component in your app. This prop is allowing us to test part of the contract, the external behavior.

Stella says, love the point about checking if the error message on test failures are good enough. This one. So I've heard about that for years and it didn't sink in for a long, long time, but thankfully react and react native testing is challenging and sometimes things fail or succeed and you don't know why. And so finally I got it ingrained. Like, okay, let me make sure the test message, the failure message is good. And if I miss it and then I see it fail and I'm confused, it's like, okay, let me get the message better as well as fixing the thing that broke as well.

So let's go back to the slides. So here is a probably fairly accurate unless I've changed something, form of the, oh yeah, I'm missing the screen in the import. That's one detail that's different, but more or less this is our test. So let's look at a few parts of it again. So we have two calls to fire event methods, fire event change text and fire event.press. So fire event is something you import from our NTL. There's three of the most common actions or user interactions that are available as methods on it. Press change text and scroll. Scroll I tend to use a lot less often but that may very well be useful for you. There's also a general form where you call fire event as a function and you pass it the element. And then it's the second argument event name that can you can pass press or change text or scroll or any other events, even custom events that your components might have. And then there's an event data you can pass along with whatever arguments are really passed by that event you can pass them in your test. So the press change text and scroll are just convenience methods for calling fire event in this way because these three are the most common. Some coworkers and mine at a hackathon recently were working on making a contribution to React Native Testing Library. And we saw in the code. I would recommend you to check out the React Native Testing Library source code. It's pretty straightforward, as far as source code goes. And you can very clearly see in there that these methods are just alias, or they go to calls, they call straight through to fire event and just populate the event name. So whichever way you get access to it, basically you're firing events on the React Native Element in the same way. Then once we fire the events, we are checking what's in the text field. And in this case we get the text field by get by placeholder text, as we did above. And we use to have prop to check the value as the empty string. It's provided by just native. You can just say, expect some element to have prop, prop name, prop value. I will say that checking the value prop of a text input is the only thing that I've needed this for. If you were testing a native component and you wanted to see what props were passed to it before you went into native land, that could be another reason to call to have prop. And with that, we have the user interactions are complete. So we've got three of our four types of inputs and outputs that are covered. And we're actually going to do the fourth one now. I haven't the third part, the third part of our talk in the third exercise kind of go beyond this test and make contract framework. So we'll kind of move on from there. But we're going to go to calls to function props now, and that's going to allow us to test the other half of our forms functionality. So in order to test this, we're going to use Jest mock functions. You may have used these if you've worked with Jest before, but here's a link to the docs. And again, it's available on the workshop webpage to get to this on reactnativetesting.io. So to create a new mock function, I want to explain it a bit first before we jump in the code to see it. So you call jest.fn to create a new mock function. That's all you need to do, but I've gotten into the habit of, when I remember always calling mock name. And you can give the mock function a name. Oftentimes I'll make that correspond to the name of the variable I assign it to, but that's going to give you actually slightly more clearer error messages. And we'll see that in a minute when we code it. When you have a mock function, you can say, expect my mock to have been called. So that confirms that, when you passed into a component, ultimately that function was called. So it doesn't have any functionality automatically in itself, but it just confirms, hey, did somebody called me? It's a function that allows you to inspect the call history of it. More frequently, you can call to have been called with. And you can pass the parameters or arguments that were passed to that function. So you can actually check the values of this called with. And there's actually a linter that recommends always using to have been called with. Because usually you care about the data that's passed to these functions. Sometimes you don't care about a certain value though. Maybe it's coming, getting called from a third party. And so you can say, expect it to have been called with expect.anyString, or expect.anyDate. And so that's ways to say, I expect there to be a certain argument here, but I don't care about exactly what the value is. So let's go to the code and see just mock functions in action, because they're gonna be very helpful to test this output. The output of, which is even more abstract than user interaction. The output that is, I call the function, I called out to a function and I sent data to it as well. So we're still on our new message form and you can't see it in the UI, but, we saw in the code that one of the things that happens when there's an input of tapping send, having typed in text, the onSend function is called. So that's what we wanna test. So let me get my notes up. All right, so I'm gonna create another test for this as well. For the reasons that I gave before, I like to have small focus tests that just test one behavior of the function, whenever I can, 1.1 behavior of the component, whenever I can. It calls the SendHandler. So that's the behavior we're wanting to confirm. These first parts, we can actually copy and paste, render, fireEvent, fireEventPress. So change the text and then press it. And then we can say, expect SendHandler. Handler. Oh, actually I forgot to put in the SendHandler. So we need to create that mock function. Sometimes I actually write it in this order, actually. Like I'll write the assertion first, and then I'll figure out the code that does it. I need some kind of SendHandler, and I wanna check that it has been called with, and I want it to be called with, hello world, the text that we passed in. You can put that in a variable as well. Expect SendHandler to have been called with this. So SendHandler is not defined, so I need to define it. So let's do that. Trust function.

13. Testing the SendHandler Function

Short description:

We can do test-driven development. We confirm the output when it hasn't been called. We use mockName to provide clarity in error output. We pass the sendHandler prop to new message form. We use a mock function with no implementation. We ensure that sendHandler has been called. We remove duplication by using a custom setup function. We extract the sendHandler to check it. One assertion per test provides a clear outline of functionality. We encourage one behavior per test and removing duplication. The test calls the send handler and checks its invocation.

And let's leave it without the mockName now, so you can see in the TestOutput what happens, and you'll see the benefit that comes from giving it a name. So this actually, at this point, we can do test-driven development. Well, no, this is not test-driven development, but we can save and confirm the output here. When it hasn't been called, we say, expect TrustFunction to have been called with, expected it to be called with, hello world, but it was called zero times instead. It wasn't called one or more times with this argument, it wasn't even called at all. So from this output here, expect JustFunction to have been called. That's not the clearest. If you look down at the code here, we can see that in the code it's called sendHandler, so that's helpful. But it would be nice that this line should sendHandler as well. And that's what we can do by saying, mockName, sendHandler. We put in a name there, and save, which says, expect sendHandler to have been called with, expected. So that's just a little bit more extra clarity, and I tend to like that, sometimes in tests, we'll have multiple mocks going on, and there's some complex code being handled. And so this name is helpful to have clear error output. But now, the reason it's not being called is because we're not passing it into new message form. There's nothing to call it, so we'll pass it in. We've got the onSend prop, onSend, call the sendHandler. And again, the mental model here is that with new message form, we're not connected up to the rest of the app. It's not sent back to a server or anything like that. The responsibilities, the contract of new message form is simply to call whatever onSendHandler was passed in. And so we're using a mock function here that does nothing. We don't want to simulate the behavior of the larger application. We just want to simulate, there is a function here that can be called. And so that's why a mock function with no implementation is very helpful. All of this test of new message form cares about is that sendHandler has been called. That's the contract of new message form. Let's save and see if it passes. It does, don't trust it. Let's see if it fails now. So if we comment this out, we don't call onSend. The test fails. We says, expect sendHandler to have been called with hello world, that it was called zero times. Now let's try to break the arguments. Let's say onSend is called, but we pass in bad argument. Or you could pass in no argument. We save and now it says, okay it was expected to be called with hello world. Now we're seeing that onSend was called with no argument, not with hello world, but instead it was called with bad argument. So we're confirming that the right arguments were not passed along with them. Now when we switch back to onSend with input text, the test passes. And by seeing the test fail, we've now confirmed that the test really is covering and ensuring our component behaves the way we like. So something I wanna point out here as well, so if you do this bad behavior, you end up with this duplication where you have this setup that happens here that's duplicated. And so there are ways to actually remove this duplication. So my advised approach has changed over the years to follow test and library recommendations. And so the way that it's recommended now is to use a custom function here to do the setup. And you could say, you might call it, give it a name like render and send message. So what we can do is in this setup function, we could copy this shared part. The shared part is we render and send a message, copy that up here. And let's just replace the first one to start, render and send message save. And let's make sure the test still passes, it does. So now it's not exact duplication though because in this test, we have a mock as well and that's passed in, so that is not used for this test, but actually it doesn't hurt for it to be present. It actually kind of makes it more consistent for it to be present. So it doesn't hurt us at all to add the send handler to this shared setup function and then replace it down here. We are gonna get an ESLint error though that send handler is not defined. So there's a few ways you can handle this. I think actually, this is not what I had in my example code but I think here it's common to return it. This is kind of the most explicit ways that explicitly call a function and explicitly return a value. And then you can say constsendandler equals render and send message. So this first test doesn't care about any of this value. And so we don't do anything with the return value but this test does care. And so we extract the send handler so we can check it. So let's now save. Our test still passes. And I guess we can try to break it if you wanted to be super paranoid, you can trust me but briefly, if we could call that one of the test breaks, if we comment that out, the other test breaks but if both of those are present, the test passes. So we've now done this duplicate setup. So in this point, we're basically saying in the pressing send scenario, here's how to do it and then here's the two things that we're confirming. One assertion per test, we're confirming that the value is clear and we're confirming that the send handler is called and in Jest in particular, one of the benefits of one assertion per test is that you get this nice descriptive outline of the functionality. You can see a new message form and pressing send, it clears the message field and it calls the send handler and you can see if one of those breaks and the other doesn't, you can see very quickly in the output exactly what's going on. So that's why I prefer this one assertion per test setup. As another alternative, you can just combine it together and have one test that checks all the things but I find that that makes it, you tend to gloss over details when you do that. It's very tempting to do so. So I'd really encourage you to try this one behavior per test, this one assertion per test approach to testing. And if you do it, this is a way you can remove duplication. All right, let's go back to the slides. So here's our test again. It calls the send handler. This is before the removing of the duplication. We set up that send handler. I looks like in this example code here, I actually had message text as a variable. So let's see the differences there. But I set up the send handler as a jest function. I pass it in as a prop to the new message form. And then I changed the text and press the button as usual. This time when I'm checking is expecting the send handler to have been called. And in this case, I actually use the message text variable. One of the impacts of that is you make sure there's not a typo between when you type it in and you bring it out on the other side. Oh, look at that though. I didn't use the variable there. So it's actually not adding a lot. Yeah, in this case, what I'd recommend is saying, I'm putting message text in there and in there. So you can see when message text goes in, message test comes out, input and output. I want to go back to this point.

14. Testing the New Movie Form Component

Short description:

Mocks are not testing implementation details when used to test the contract of a component. Component testing is valuable for ensuring reusability, and mocks are an essential part of testing the contract. Testing requires thought and consideration, and blanket statements about mocks testing implementation details are oversimplifications. The next exercise involves testing the new movie form component in isolation from the rest of the app. The component is not directly wired up to the API, and the goal is to specify its behavior through tests. The test focuses on pressing the send button, clearing the new movie title field, and ensuring that the createHandler function is called with the movie title.

Are mocks testing implementation details? This is something you can hear. You can hear blanket statements that you should avoid mocking or you should never mock because if you're using NHS mocks, you're testing implementation details. Your user doesn't care about mocks. Your user cares about how your application works. I disagree with this and let me tell you why. Certainly mocks can be overused. If you're mocking everything you can create tests that are highly coupled and hard to interact with. In the testing philosophy that I'm talking about right here, we're talking about testing the contract. And so when you're testing component by component, you're not focused on what the user sees and cares about and expects, you're focused on what the rest of the applications code cares about and expects with regards to your component. You care about when the rest of the application or your user gives certain inputs, what are the outputs to the rest of the application. And so if you wanna confirm, if you're looking at the contract and you're looking at the, this component does not know about API calls or whatever, or calls to a context or Redux or whatever, this component knows about, I called a prop function passed in. That's not an implementation detail of the component. That's the contract. And so if you're just end to end testing, you just test what the user sees and cares about. But if you're testing a component, you should test the components contract. And so mocks are not testing implementation detail when you're using it to test the contract, it's part of testing. It's part of saying with this component contract with the rest of the application is, is that this function prop is called. That's precisely what a mock is. If you didn't use a mock, you would use some other kind of hand implemented way to test that or else you wouldn't test components at all, you just test the whole application integration. So I think component testing is valuable. I think it helps to make sure your components are reusable. And mocks are not an implementation detail in that case. They're an essential part of testing the contract. You can make your own decisions on that. You should think about it. Testing is not, testing unfortunately takes thinking. There's no easy answers when it comes to testing or I'm trying to give you helpful starting points. But it's like testing takes thought. So think about these things. Think about them in your application code and see. But I would say, when people say blanket statements like, if you use tests and you are always testing implementation details, that's an oversimplification, I would say. Think about it for yourself.

So with this, now that we've tested calls to function props, our test of the four things that I included in testing the contract are now complete. We've seen what to do with props and user interaction events coming into your components and rendered UI coming out to the screen and calls for function props going out to elsewhere in your application. So we've now covered all these inputs and outputs. And now it's time to do another exercise. So this is in the same repo, intro to Rntl exercises, exercise two. And in that Markdown file again, there's this link to help the interaction section of this page on Reactnativetesting.io. Let me show it in front of us. I know you all can read the exercise, but just to get it in front of you, it's exercise two. And in this exercise, we're going to add test for the new movie form component. Again, this is a fully integrated app where it's our movie list app, and this is a place where in an app, yeah, I've got this new movie form. I would like to test the form as a component. In this case, the component is already written and functioning, but there's not yet any tests for it, not even a test file. So create a file, new movie form.spec, add all the tests you need to fully specify the component's behavior. And again, I kind of described the component. It is a, I do have some more, a little more styling, tiny bit more styling, but it is still a text input and a pressable that you're interacting with in a similar handle set. Yep, so be sure to test the component in isolation from the rest of the app. The component is not directly wired up to the API, and we don't wanna wire it up to the API in the test. We want to limit the test to just the responsibilities of new move. All right, so let's take that away. I'm gonna go for eight minutes this time. That's convenient. We're at 4.32. Let's go into the 4.40 to do exercise two here for a new movie form. And again, I, especially if I said something and you were like, oh, I don't know about that. I think it would be better to test this in a different way. Please do, please try it. And I would love for you to share your thoughts as we go and we look at the way I did it, we can talk about the way you did it, would love any input if you would like to share. So with that, I'm gonna go on mute and let's take eight minutes to do exercise two. One more minute and then we will take a look at a solution. All right, let's jump in. We'll take a look at the way that I wrote this test, and then again, I would encourage you, if you would like, share your thoughts on if you approach something differently or if you have questions or concerns or things like that with the way I approached it. New movie form.spec.js. Yes, okay. So again, I have pressing send as the basic aspect of the approach here. I did pull up the message text as a variable. Oh, that's not actually... This is copied over from the widget. Hello world is... Maybe there's a movie called that because of computers. But I think let's give it more of a realistic name. Incidentally, when it comes to naming test data, some folks would say things like fake, fake movie title. Even the variable name is not correct. In the world, let's improve this test and they'll be right better for the next time. This is movie title. So yeah, when naming test data, sometimes folks would say fake movie title, something like that, obviously fake value, but sometimes it's more useful to have something realistic, let's say like new Star Wars or something like that. So you can go either way. I'm gonna go to fake movie title for now. I've been leaning that direction lately, I don't know why. So here in render and save, I took a bit of a different approach here. So instead of returning the createHandler, I set a let variable here out in the context of the described block. So that means when I assigned a createHandler in this function, I can then access it down in the test. That's a bit more indirect as far as reading the test, it's like, where's the createHandler come from? But when your test is very small and focused, it can be pretty easy to see it, but you could also return that value. So we create createHandler as a just mock function, we pass that in new movie form the onCreate, then we change the text, enter the movie title, and we press send, press Save, yeah, I'm not sending, we're saving the movie here. And first, it clears the new movie title field. So we're confirming that it has prop value of empty string. It's an interesting line breaks here due to prettier, but that's all. And then we ensure that it calls the createHandler, expects createHandler to have been called with movie title. So yeah, that is a pretty similar.

15. Testing Effects and Module Mocks

Short description:

In this section, we explore an alternative approach to writing tests with multiple assertions. While it is possible to have multiple assertions in a single test, it can make the test less readable and harder to maintain. We discuss the benefits of having one assertion per test, which provides more focused and precise tests. We also review the progress made so far in testing the contract, inputs, and outputs of components. After a short break, we move on to the next topic: testing effects and module mocks. We emphasize the importance of testing the end result or contract rather than the implementation details. We address concerns about hitting real server APIs in tests and suggest testing the application in isolation from the backend.

Let me show you the alternative of what it looks like if you have one test that has multiple assertions in it. And you can see what you think of that. So let me save these changes. So I have these to improve this for the next time I do this workshop. Yeah, so say if instead of one test per assertion, you wanted multiple, you could say it, I'm gonna name the test. So here's the way... You might ask why does jest have it as an option? I just wanna say test, let me say test pressing send. So let's do that for now and coming back to it. Test and it work just the same. It's just a matter of conceptually how it works. So you could say, let's put the movie title in there, the create handler. We go ahead and assign to the create handler there. Maybe we say const. So we set it to a variable. It's not gonna change. We can just say const and we render and fire event. And then we do the assertions. We expect this. We expect that. Let's just delete out the separate version. Actually, let's keep the separate version. We'll skip it though. Save and format it. So yeah, movie form pressing send. It is a test and it works. We do this set up and then we do two expectations, one after the other. So that's totally valid and I mean, I absolutely do. I'm not saying that I never do this. I absolutely have tests where I assert on multiple things, sometimes very related, sometimes not so related. So the benefits of this is you can kind of read it from top to bottom. You see some things happen and then some things get checked. So what are the downsides? Downsides are you don't see these two. Let me take off the skip because that makes it kinda hard to read. You don't see these two descriptions of the two things that happen. And so you have to read code to see. You have to say, okay, expect screen.getplace by placeholder text to have prop value empty strings. It's not that hard to read when you're used to React Native testing library. But it's a lot easier to read clears the new movie title field. And so I think it's helpful to have descriptive test names like this. So in this case, and if you said it, how would you title this? If you wanted to provide more information in the test time, you say it, it sends the movie title to the createHandler when pressing send. Then you can say and clears the text field. And at that point, that's the same as is very long. And so that line breaking easy, it kind of off, but it's like, it's hard to visualize. So the slippery slope you end up going down is you say, oh, it clears the text field. We're gonna test that, but we don't need to put that in a test title. That's not important. But then, so then you're like, oh, well, what are all the assertions? You're just kind of fuzzier on what in all you're asserting. You just have a big mass of code that's asserting things. And I tend to find that that can be less precise. I tend to focus less on the test as I maintain and change things. I tend to lose track. So that's why I really prefer one assertion per test when I can to give me these focus tests scenarios. All right, so that's all, yeah, just reviewing again. We finished testing the contract. We've tested the inputs of props and user interaction events and the outputs of rendered UI and calls to function props. And that's really good progress. So next time, next time this is on a live stream. We're continuing on. This is a one-time workshop. We're gonna take a five minute break at this point just in case people have been working through the exercises and need time for a bit of a rest. And after this, what we're gonna be talking through is effects and module mocks. How do you test the results of effects and hooks actually in general? And how can module mocks help with this? All right, we're back. Let's get going. Effects and module mocks, our third and final section here. So how can we test effects? Use effect or really any other kind of side effects that happen as a part of our components. Well, really test the effect, test the result. You don't need to check that a user effect ran or use query from React query or whatever from Apollo. You check the effect or result. If data came in, is the data displayed? If a function is called, is that function called out into? So that's what you test not the internals of whether something is implemented as a hook or implemented in use effect or React query or some other API you're testing the end result or the contract. So specifically when you think about data loading effects that hit the API web service requests is if in a test, you know, unless you change things, if it's wired up to, you know, statically import your code that makes the call, it's really gonna hit the real server API. So is that a problem? So here's what I'd say on that. There's all kinds of different options you can use. So think about this stack that I have shown in the diagram here. You have the rest of the code in your app and then ultimately calls into an API client module. I'm assuming you have some kind of code in your app that wraps up where the web servers requests go. So it's not always the case, but that's just for the sake of argument here. Then it might call to a network request library, like Axios. And that goes through to underlying platform HTTP request support. In our case in React Native, that in turn goes out to native iOS or Android or web or elsewhere, HTTP request support. And that goes out to a real web server to make that call. So should you mark this out? I will say here that there is value in testing that your React Native app actually works with a real backend server, but in general, what I've found, and I think I might have a slide later that gets into more detail, for automated testing, that tends to not be that useful. Certainly, you know, Reactive testing library tests. Often you'll wanna run these on continuous integration. When you put up the pull request, you run your RNTL test suite to make sure nothing's broken. So you want this to be reliable. But if those tasks are hitting an external server, it tends to not be reliable. Internet connection can flake out, like just happened to me just a few minutes ago. Data can change on that server that affects your test. The URL can change for the server, and so that makes things flakey. So I recommend with RNTL tests, to test your application in isolation, from your backend.

16. Introduction to Mocking in Software Development

Short description:

There are various options for mocking in software development, including just module mocking, library-specific mocking setups, generic libraries like Knock or Mock Service Worker, and mock web servers. In this workshop, we will focus on just module mocking as a portable solution for JavaScript and TypeScript. It allows you to mock out any module using Generators, providing more flexibility and power. This approach prevents you from getting stuck when testing third-party libraries without proper mocking solutions. Just module mocking is a generic solution that can be used with any library or technology. It is well-documented on the mock functions page, and it involves importing the module you want to mock and specifying the mock implementation.

So you need to mock out at some point, how can you do that? There's actually a lot of different options. And I wanna lay them out to you on the diagram, to show the differences that they make. I'm gonna do them all at once, I didn't animate this, it's just all one big slide. So if the rest of your code is calling into a module, a JavaScript file that you've written, you can use what's called just module mocking to say, Oh, don't use the real module, use a fake one that I provide, and then you use mock functions. Or if you're calling out to an HTTP library like Axios, there may be a library specific mocking setup, like Moxios which is an Axios specific mocking setup. Or regardless of the HTTP library you're using, if you can mock things out before the underlying HTTP Request Support, by using a generic library like Knock or Mock Service Worker that can mock all of your HTTP requests or intercept any of them. So that mocks out regardless of whether you're using Axios or something else, or if you change it in the future. And then from there, another option is to actually have a mock web server. So instead of having an actual running web server, there's a real HTTP process, but maybe you just build a lightweight with Node. Or maybe there's tools, I don't know the names of them, but tools that will just serve up static JSON files, but it's an actual running real web server, so you're not intercepting the requests. The request really is going out to a real server, but maybe you boot it up on CI and it's running locally there. And then that way, you aren't risking the network being down elsewhere. These are all different options with different trade-offs, but the way that I wanna teach you right now is actually the very first one, just module mocking. It's not because the others are bad, I actually use Knock on my open source React Native apps. So there's a lot of value there. The reason I wanna teach you just module mocking is if you're using JavaScript or TypeScript, like you are if you're using React Native, you can use just module mocking, no matter what. No matter what library you're using, if you're on HTTP, GraphQL, Couch DB, Firebase, whatever it is, you can mock out a module using Generators, and that gives you more power. One of the ways you can get stuck in testing is if you're like, oh, I don't know how to test this library. Their architects or the lead developers chose this library and I don't know how to test it, or somebody two years ago chose this library and they didn't write any tests and I don't know how to test it, and you Google around and you hope that somebody has written a way to mock out that library, and if you don't, you're stuck. And you say, I guess I can't write tests for this. And that part of your app is untested. I don't want you to be stuck like that. I don't want you to have to find a new testing mocking solution every time you do something else. I want you to have a generic solution and just module mocking is a generic solution. It's not the best for everything, and maybe for your HTTP stuff, you will use not her mock service worker, but just module mocks is a portable solution that I want to equip you with. And so let's do that together. Just module mocks are documented on the mock functions page. Basically it's instead of just instantiating a mock function in line, you have another module that you're importing from and you say, I want to mock that. And we'll see how in just a second, which it is right now to the code.

17. Loading Widgets from a Web Server

Short description:

In this part, we explore the code that handles the loading of widgets from a web server. The widget container uses the useEffect hook and an API module to retrieve the widgets from the server. However, when running the test, the widgets are not loaded yet, resulting in a test failure. We investigate the issue by inspecting the DOM state using the screen.debug API. One approach to fix the issue is to make the test wait for a certain amount of time before checking for the widgets. However, this approach is not recommended due to non-deterministic test results and slower test suite execution. Instead, we recommend using just modulemock to replace the API call with a mock implementation. By mocking the API module, we can control the response and ensure that the widgets are present during the test. We demonstrate how to mock the API module and discuss the downsides of other approaches. The text also mentions a warning about not wrapping updates to the widget container in Act, which will be addressed later. Let's proceed to the next section to continue exploring the solution.

All right, so the functionality that we're going to look at here in lecture three is when we click into here, these widgets load on the page here. So this is loaded from a web server. There's a bit of a delay. It's a little faster now actually. But this is loading from a webserver. So let's see the code that handles that. You can probably guess how it's going to look. It's very simple. So in this widget container, I'm using useEffect. The old school way, the pre React query way, but this testing approach will work for React query or anything else, incidentally. But I'm getting this widget from the server and I'm using this API module that I've created.

In this module, I'm creating an Axios instance, hitting a backend server, and then I'm passing it out. So because the Axios instance is returned, my module has the same API as Axios, but I could change that in the future if I wanted to just use fetch or something. And port the API, call API.getWidgets. And then if I get a successful response, I take the response.data and I set that as the widgets here. And then those are rendered out in a flat list because we're in React Native. Not a flash list, that's another option that's out there now, but I think that would work as well. I haven't tried it. But yeah, so we have a test here. Whoa, and we have some language server protocol here. We don't want the heads, let's take that away. We have a test already written. This is a widget container. We're testing that we load the widgets upon first render. So we render the widget container and then we check for widget one to be visible and widget two to be visible. So let's run this test. We run it and it says unable to find an element of text widget one. So that's unfortunate, we were hoping to see that and we do see that when we run the real application, this test is not realistic. What are we gonna do? Well, we gotta dig in a little further. And I just remembered that I don't have my notes in front of me, so let me pull those up. Yeah, so the reason why in this case is because we're not actually waiting for this to return. I made this intentionally have a bit of a delay when you pull it up, but even if that was not the case, it's an asynchronous call. And so immediately when the widget container renders, it does not yet have the data. There's still loading that needs to happen. And so we can inspect what's happening. What's the state of the DOM of the JSX tree at this point, using API called screen.debug. We add this and say, you don't wanna keep this in your tests and that's why there's an ES link that says remove that when you're done. But it outputs what we see. So we see as a view with a React scroll view, that's what a flat list is under the hood. And then there's a view underneath that, there's no children, there's no contents. So by the time we get here to be checking for these widgets, they are not there, they're not loaded yet. All right, so how can we fix this? I'm gonna show you some approaches I don't recommend first that involve actually hitting the server. And then we're gonna see some of the downsides of that. And then we'll show the approach that I would recommend instead. So one way is to make the test wait for some time before it checks. And so a way you could do this would be, it's kind of cumbersome because it's not recommended, but you could say return a new promise. So we're gonna make our own promise. And then we're gonna tell it when to be done. So in this promise, we're gonna set a timeout. Call back hell here, we'll wait for two seconds, I think should be enough. Then after waiting for two seconds, then check that these elements are present and then call Resolve, which is gonna resolve the promise, which in turn tells the test that it's fully done. And just when it gets a promise back, it's gonna wait for that promise to resolve before it treats the test as finished. So let's save and see. We save, takes a little while because it's waiting for two seconds. Oh actually, it waited even longer. So this looks like this, still didn't actually succeeds. This one exceeded time of a 500 millisecond for a test. This is not supposed to happen. How am I, what am I doing wrong here? Is it not? That's the API, the same API that's been called here. Could be because of my internet here where I'm presenting it from. Let's try again. Oh yeah, I guess it was spotty. I did not mean to illustrate that, but check it out. One run failed, the next run succeeded. So that's the non-determinism that is not good to have in a test. And so here it did pass because ultimately that widget did show, those widgets did show up. But note that I have a warning here. An update to widget container inside a test was not wrapped in Act. Let's ignore that warning for now. We are gonna come back to it because this can be painful in react and React native testing. Let's look at a few other downsides. So maybe the request would return, but it would just take longer. Maybe that's what happened in the previous run. So that could cause the test to fail sometimes. You could get around that by increasing this timeout even longer to 10 seconds, but then at that point, you're always waiting 10 seconds. So your whole test suite slows way down when you have more and more of these. Also, if the remote server goes down or is just not reachable, your test will fail. So as an alternative, let's use just modulemock to replace the API call with one that we create. Let's return the test to the state it was in before we made these changes. We're gonna save and see, oop, I got too many characters here. Save, and we just get a failure right away. Now we're gonna mock the API module. So the way that we do this is, let's see, first we say jest.mock, and then we pass the path to the module, the same path we would import. In this case it's, in the same folder, API. So let's kind of mock out that module. And actually, that's all we need for right now. So let's save this, and see what happens just when we just mock the API module. Again, that's the module that's imported in the component itself. We save, it runs, it says the above error occurred. Sometimes the above error is below you, so let's go down to look below us to find the error.

18. Testing the API Module and HTTP Endpoint

Short description:

Now the test is passing. We mock the API module and ensure that API.get returns a promise. We provide the necessary data for the API.get call. We use the screen.findByText API to wait for the element to be found. We confirm that both elements are present. We discuss the warning related to asynchronous behavior in React Native Testing Library. We also mention the importance of checking the correct HTTP endpoint when using just module mocks.

And it says, type error. Can not read properties of undefined reading Venn. So what this is saying is, now API does have a get, it's able to call it, but what gets returned is not a promise. It doesn't have a dot, Venn. So this is because when Jess mocks a module like this, I still haven't fully been able to get it into my head, just cause modules can be setup in so many ways, default exports, and named exports, but in this case what's happening is, the API module is getting mocked, it actually sees that there's a get on it, but it doesn't know that get returns a promise, cause that's too much introspection for it, and it's maybe more than a testing lib, mocking library should be doing. So instead, we wanna tell it that API dot get should return a promise. The way we can do that is to just import API in the normal way in our test here, and now we have the API object, and then down in our tests we say API dot get, uh, and then we use this API called mock, this method called mock resolve value. This basically says, API dot get is a jest mock function, that's automatically created for it, and what we're doing is, looks like that hint there is not correct. We're calling mock resolve value to say, when API dot get is called, return a promise that will resolve, and it just resolves to undefined at this point, cause we haven't passed an argument. So this should cause API dot get to return a promise that does have event, and in fact will resolve. Let's save it and see what happens. Alright, now our test continues. It doesn't blow up anymore, but now we're back to the same error. Unable to find an element of text, which it wants. And so, this is because API get is now returning a promise that resolves, but it doesn't, we don't have any data that's provided with it, and so we wanna provide that data. So we're gonna, in this mock resolve value, we can pass the value. We're gonna pass an object, it's gonna have a data key, because that's the API that axios uses, it returns an object that has a data key, and we don't need to provide a full axios response object, that's integration, they kind of connect everything together. We're mocking it out. We're gonna mock it out with only the API of what's needed here, response needs response.data. That's all. Data has some objects, we're gonna give them IDs so that the flat list will work, the react native will have keys, call it name widget one. And then here, ID two, name widget two. Let's see if I've got this right. Let's save. And we've got an act. All right, so we've got the act warning again. The problem is, it's no longer rejecting, but we're getting null output at y. And we're getting this. Yeah, we're still getting unable to find L over text. Let's do screen.debug again. So we can still have the same results. Even though we're mocking this out and we're turning the correct values, we're not waiting long enough for it to show on the screen. So how can we wait long enough? So the way we can do this is using a different screen API called findByText. We're going to update just the first one of these. We're gonna make this an async test. And we're going to say await screen.findByText, widget one. What this will do is it's gonna wait until, wait until this element is actually available, like until it's actually found. So it won't be found right away. It'll keep retrying. React Native Testing Library will keep retrying over a period of time until it is found. And actually I should update this for the new to be visible. We can wrap this with expect to be visible to get that extra checking as well. This await in the middle of a statement you may not have seen that before, but this will just await right here. And then once a value is present, then the rest of the statement will evaluate. So that actually works. I'm going to talk about why we don't update the second one in just a second. Let's save and see what we got. It now passes, let's get rid of screen debug. And again, let's not trust it, but let's test it to make sure. Let's see what happens if I don't return widget one in the response. Then we get unable to find an element text widget one. Cool, okay. So we're confirming that actually comes in. What if I hide widget two and don't include it? Unable to find texts with element, with text widget two. Cool. So it's confirming that both of them are present. Now why did I await the first one and not the second one? That would be more parallel to do that. The reason why gets to asynchrony in React native testing library. It tends to throw warnings up if you're awaiting something, but that thing is already true. Let me try it here and see, I mean, it may be that in this case it would pass, but I expect we'll probably get a warning. Let's try. Oh, it does pass. Well, I guess I could have tested that before. So yeah, so in this case, waiting to find both of them will work. I will say in React native and in React, I have seen cases though, where we get warnings in that scenario where it's like, hey, you're waiting for something and you don't need to be waiting for it. And it's kind of a unclear confusing warning actually. Plus when you read this, this kind of suggest to the reader, we're waiting for this and then we're waiting longer for this, but that's not actually the case. As soon as widget one is found, widget two will be available as well. So there's some benefits to writing it this way to make that clear. But in any case, just a warning to you about extra asynchronous. Yeah, we remove our debug statement there, so that helps. So there's one more thing to test as well though. And this is something important to keep in mind when you're using just module mocks, or if you're using another test library, is good to think about another HTTP mocking library. What happens if I go in here and I get the HTTP call wrong? What if I call fidgets instead of widgets? Let me save. So the test still passes. The test is not ensuring that I'm calling the right endpoint. And the reason why is because this just module mock doesn't know anything about HTTP. It just knows that I said, when API.get is called, mock the resolved value, and it doesn't care if any arguments are passed or if they're the correct ones. So I want to set the test up to actually fail now that I have the wrong call in there. How can I do that? Well, API.get is a just mock function. And so we can use the APIs we already know about. In here we can say, expect API.get to have been called with widgets. And actually let's add Stella's note in there, times one. So I didn't show this earlier, but this is what Stella was getting at. You can check that API.get was called one time, and it was called with these arguments. That's the safest to check both the quantity of calls and the arguments that were passed. And the number of times it was called should succeed, but call with widgets is what should make this test fail. I save, and it says yes, expected to call with widgets and got fidgets instead. So let me change that back. Now the test is passing.

19. Testing API Calls and Module Mocking

Short description:

We need to call the API with the correct arguments and ensure the correct endpoint is being called. Just module mocking does not allow combining assertions for API calls with arguments. However, it provides the flexibility to test APIs with various underlying technologies.

So this is important to check because this is important output to our component. When we call, and I mean, I guess it is, I mean, we're calling this API call, we're calling that function. We need to call it with the correct arguments. We need to make sure that the correct endpoint is being called over there. That wouldn't be the case if your module didn't have any arguments called in. If it was like myAPI.getWidgets, there's no arguments. And so there's nothing to assert there, but when you do have arguments that need to be correct that are passed, you want to assert on those. One of the downsides about just a module mock API is that I can't combine this up here. It would be nice to say API.get if it's called with widgets resolve with this value, but I'm not aware of a way to do that in Jest mocking API. The upside of this though, is that whether this API has GraphQL or CouchDB or anything else under the hood, you can use Jest module mocks to test it.

20. Testing the Movie List Component

Short description:

In this part, we discuss mocking the API module and testing the behavior of the movie list component. We focus on data loading and display behavior, excluding data creation behavior for now. We encourage the use of just module mocks to avoid making real API requests in tests. The movie list component is wired up to the real API, so we import and call it. However, we want to thoroughly test the component's behavior without hitting the server. Since the movie list component uses the movie row component, which is already tested, we only need to ensure that the movie list component itself is working correctly. We provide a link for further information and suggest additional steps for testing movie creation and exploring different approaches to test data loading functionality.

All right, let's go back to the slides and process what we've seen. Here is the version of this code that I wrote the last time when I was making this slide. Let's see how similar it is. As we go through again, you can see here, we're importing the API so we can have access to it, and then we're mocking it out. And Jest actually runs these in the inverse order, it's hoisted up to the top, such that the mocking happens with this API object I have is the mocked version of it. And then what do we do with it? We call mock resolve value to set the value that should be called. With if and when API dot Get is called, return a promise that resolves to this response data that comes from the server or wherever. Then we call await screen.find by text to wait for the first one. And then oh, I've got a query by text there that should be a get by text. I'll take a screenshot of that, remember. Expect screen.get by text to be visible, confirmed that both widgets are showing up. And then make sure that we call, when we called API dot get, it was called with the right arguments because otherwise, our test will fail, or our tests will succeed, but the real call will fail. This is one of the concerns folks have with mocks is like, oh, it's unrealistic. You're getting your test is passing, but in reality, it's failing. It's like, well, yeah, if we wanna isolate from the server, we're not really gonna hit the server if we want that reliability. We need to make sure we set up the mocks in a way that they're thoroughly testing the contract, they're confirming that the output's, and they're sending back the right inputs. So, to review what we saw, in, when you have a mock module, you can call that module dot a function on it, like my funks, you can call mock return value. And so, that will just return a value immediately. It's not a promise, it's just a value synchronously returned. If you wanna promise, you can call mock resolve value, or mock rejected value. And mock rejected value is really useful for testing error scenarios, when it's kind of hard or tedious to simulate errors coming from your server. It can be really thorough, and one of my favorite things about writing tests for my code, my front end code, is to be able to handle all the edge cases like error scenarios, cause I just hate doing that manually repeatedly. I'll do it once, but I don't wanna do it every time I make a change to the code, like there's no way I'm gonna have the discipline to remember to always retest all the error scenarios, the waiting scenario if the request takes too long, like, goodness sake, I'm gonna automate a test for that cause I'm not gonna have the discipline to do that by hand. I'll pull up the chat here to make sure I have access to it. So we've seen getter functions before. GetByText, GetByLabelText and GetByPlaceholder. Here we saw find functions. And so there's a find version. Basically you can change the prefix get or query or find, and you can change the suffix by text by label text by placeholder. All the combinations are there. And these find functions allow you to wait until something is present some amount of time. I think we saw that the default timeout was five seconds, which should be enough time usually, especially if you're marking these things. But that will allow your test to wait for something asynchronous to happen. And this is often very important when we're loading data from the server as is commonly the case. So now we're there with exercise three. And this is our final exercise. After this exercise, we just have some wrap-up thoughts and some big picture thoughts about component testing and how it fits in. But let's review these exercises real quick, and see. The lecture, here's the exercise three. Effects and Module Mocks. In this exercise, we're going to add tests for the movie list component found in source movie list. So, here, this is our parent component. And let me actually run this app so you can see it. I should've run it before. It would've been nice. We're going to open it up. Reload. Now, we have our movie title map. It loads Vertigo, sound of music, Ringo. So, movie list is the overall screen component that includes all this stuff. So, if you take a look, we have an array of movies. We have a use effect to load them. And so, this is the main part we're going to be testing. Like, are we loading from the server successfully? We actually have a handle create here as well. So, that's a stretch. We don't need to do that right now. But, and then, we're rendering it out. So, here, we call through to the lower level components. We have the new movie form. That's included as a part of it. And in the flat list, we have a movie road that's rendered out. So, this is the parent component that's using these child components that are already tested. So, we want to write a test for movie list. The component is already written and functioning. Add all the tests that you need to specify the component's data loading and display behavior only. And note that the component also has data creation behavior because the new movie form is embedded in. But I would say hold off on testing that behavior for now. We'll save that for the going further section. You can do that on your own if you'd like. Really, basically, as I was setting up this standalone app, it kinda made sense to include the loading and the creation in one component, but I didn't want to ask you to component test everything all at once. We really wanna focus on the data loading to start. So, add all the tests you need to specify that the component's data loading and display behavior works correctly. This component is wired up to the real API. So, we statically import API and we call it. That's different from the new movie form. New movie form took an onCreate prop passed in and the wiring happened outside. The new movie test is statically wired up to the API module to get the movies. We don't wanna make a real API request in our tests. Maybe you do and that is okay if you do but I'm asking you for the sake of testing out this functionality and this testing approach to not make a real API request in the tests. There are several ways you can do it, but to start out, I would encourage you to use just module mocks the way we've done here. Because movielist displays the movies using movie row which you've already tested directly, because of that, you don't need to test every detail of movie row in the test for movielist. Instead test just enough to ensure that the behavior of the movielist component itself is working correctly. And then just make sure that if your API.get is going to the wrong place, make sure that your test fails. Here's the link here effects and external services, the page on reactnativetesting.io that has help if you would like to do it. And if you get done early here's some going further steps, you can test out movie creation or you can try out using mock or another approach to test the data loading functionality. And you can think about the trade offs there. So let's stop there. I think I will leave it for let's do six minutes this time because it's 519. Let's reconvene at 525. And then we'll see, I'll share with you how I approached it.

21. Testing the MovieList Component

Short description:

In this part, the speaker discusses testing the MovieList component. They explain how they imported the API and mocked the resolved values for the get request. They also mention testing the addition of a movie to the list and the importance of testing the outgoing message to the server. The speaker emphasizes the benefits of using just module mocking for testing and encourages questions and feedback on the testing approach.

And we can see if you have any input on the test. So, all right, give it a try. And if you like, and we'll see you at 525. All right, here is what I did. Let's see what happens. Take a look at the exercise. All right, so here is my MovieList test. So, MovieList, it loads movies on first render. So, that's what I want it to confirm. And here, in a similar way, as we saw in the lecture notes, I imported API. I mocked the resolved value as movie1 and movie2. This is actually not the names that come back from the real server, and so that's helpful that I can, if I was accidentally hitting a real server, the test would fail because it's not the same names. I render it, I check that API.get has been called. I actually can put this assertion right away after the render because, even though the response is asynchronous, like it's a promise resolving. I gotta learn more about the JavaScript's event loop, but it's the next tick or the next microtask that the response comes back on. But immediately, API.get has been called. And so you can make that assertion right away here. Then I wait for movie1 to appear, and then I check that movie2 is visible as well. That's how I tested it. Fairly straightforward. I did, I went ahead, I did a bonus deep integration test as far as, what did I do in there? I'm checking that I can add a movie to the list. So let me show this to you, since we have a little time, just to get a sense for it. So when we tested new movie form directly, we were checking that when we submitted it, the create handler was called. So that's what happens when you're scoping the test to the new movie form. But in the movie list, which includes having the create hooked up to the server, if we test this component, movie list, the creation in there, we need to test that API.post is called. So let's see how that looks. New movie form dot spec, no, movie list dot spec. Yeah, there we go. It can add a movie to the list is what I said. Oh, one other note going back up here. In my test, I chose to assert that the movie title showed up, but I decided not to check the new badge showing, because I already know that at the movie row test level, I'm confirming those new badges showing, and that would be more tedious to test it through here. At some point, if you test all of your app through those high level tests, they get extremely extensive, or you just end up giving up and not actually, you test somewhere in the middle of it, not all the functionality or not just the focus functionalities. So really all I'm testing here is just what happens in movie list itself. Do I get the movies? And do I render them out in some way that the movie title is gonna show up? So I'm done here. What I'm testing adding the movie to the list. This is, in a sense, repetitive, because I'm testing the form again, but really, I'm testing the wiring up that new movie form is wired up to send to the server correctly. So here, I still need to mock the result value of the get, because I do need to first load the form. But because I don't care about any of those records here, I can just have it have an empty array as the data to keep it clean. But then I mock and I say, hey, when I post to the server, return this value. I just need some ID and I pass back the new movie title. The reason I need to do that is because we're actually using the post response here. I get the movie back and then I add it into the list of set movies. Sometimes in my open source apps, I actually take the opposite approach where I post to the server, discard the return value and just reload the full list. So that's another way you might code the app, in which case your tests would be different. But in this case, I run to the movie list. I change the text, I press Save. And then I confirm right away that API.post has been called. I confirm that I've called it with the right URL, and I confirm that the right data is sent to the server. That's an important part of testing the outgoing message as well. Am I sending to the server, what's needed to really save it? And then I wait for that new movie's title to show on the screen. When that shows on the screen, I guess it does not confirm that I'm getting that data back from the server. I could be just putting it in optimistically right away, but I am confirming that the data makes it to the server and then it makes them on the screen. Incidentally, this approach would allow me to change between optimistic and pessimistic approaches where I either wait for the server to come back to put it on the screen or I show it right away. And if you wanted to see the difference between optimistic and pessimistic, you'd wanna test the error scenario as well to see, or maybe even test the waiting state. Hey, before I hear back from the server, do I see the new movie title already or not? That's some of the more advanced places you could take that. So yeah, that is our movie list spec. And again, I would just go back and say again, I love NOC, I love these other options for testing, mocking things out, there's a lot of benefits to them. One of the benefits of NOC is that you can combine the checking what is called and confirming what is returned. So that's a nice thing about the API, but I like to test JET, teach just module mocking. So you're equipped with the general solution so that whatever APIs you're integrating with, whatever libraries you're integrating with, you can mock them, you can test your components because this is a general solution. Real quick, any questions or feedback on this testing approach? Anything you would do differently or wonder if you would, or if I would do it differently? Any questions or input are welcome. And I will, I'm gonna pull up the slides while I'm waiting. Distracting image for you. Stella, we do the loading test quite a lot for spinners disabling an element, so like a submit button. Absolutely, yeah, I do the same, Stella. I try to get those because I wanna test thoroughly. I wanna test all the scenarios. And also because those are another case where it can be tedious to manually test those to confirm them. You might break them without realizing it. And so, you wanna see it really one, since you might wanna slow down your request or put in a timeout in your code to see that loading state temporarily or slow down your server. But it's nice to have the automated test covering that. So you make sure your loading state, your finished state, your error state, even your first state, when the screen first comes up before the load, maybe you have a state before the load is kicked off instantly when it renders. So, covering all of those tests gives you increased assurance. So let's talk about this crazy photo. I have this in here from before. It's an act error. I initially blamed hooks. This was when react-hooks was first came out several years ago. And we started getting these act errors that we never got with class components. And so I and we were like, oh, hooks, what have they done? It is a horror for us. This is around Halloween time, actually. It's Halloween time when we're recording this. This is my Halloween costume that year. And we blamed hooks. Not really. Facetiously, at least, I blamed hooks. But really, I mean, React's core team made it very clear, like, hey, there were problems already. Like we didn't want to break the API of class components, but when we're introducing hooks here, we want to take the opportunity to raise these warnings to let people know you might be testing in a way that something is coming out. So let's take a look at this act warning, because you will absolutely get this act warning in tests. You are the best tester in the world if you never get this act warning, and I will be very impressed and want to learn from you.

22. Wrap-up and Importance of Testing the Contract

Short description:

The ACT warning is raised when there is a state change that is not being waited for in the test. It is important to wait for externally visible results to ensure that the test is testing the final state of the component. Adding a loading state and a visual indication of completion can improve the user experience. Testing the contract involves considering the inputs and outputs of the component and what the rest of the application code expects. Testing the contract helps ensure that the component behaves as expected and can catch bugs that may otherwise slip through.

Or maybe I just have more to grow. But what I'm saying is, you'll probably see these warnings. Let's find this in the notes. Five minutes, do you think I could pull up these notes? Okay, so let's see this act warning. We saw this earlier on, but let me replicate the act warnings in a scenario like you might run into. We're back to our widget container now, where we load the widgets upon first render. Let's say for this test that we didn't actually care about what was shown on the screen, or maybe there was nothing shown on the screen. Let's say we just wanted to confirm that when we mounted this component, the right request was made to the server. Maybe it's fire and forget. So let's save this test and say render the widget container and confirm the API was called once with these arguments. Save. So the test does still pass, because it's a valid test and it is doing what we're saying, but we get this warning up here. It says, and I'm gonna read it to you, I know you can read, but I need to process that out loud, warning, an update to widget container inside a test was not wrapped in act. When testing code that causes React state update should be wrapped into act. And it shows you this act function, fire events that update state, and then I start on the output. This ensures you're testing the behavior the user would see in a browser. And there's a link to a helpful webpage. Hopefully I linked to Kent C. Dodds article on the website, if not, I need to, because it's a really good one to explain this more thoroughly. Kent is the creator of the react testing library and it was originally created the testing library suite. We didn't work on the React Native one. I imagine there's other maintainers now, but he kicked us off and set the philosophy in motion, which was very helpful. And he writes about this to make it clearly. I am remembering now that there are some things about this warning message that are not totally clear. The way it's written, it says like, hey, you fired an event in your test. You didn't wrap it in act. The solution is to wrap it in act. That's not what actually happened here. And that's not what commonly happens. More often, what's happening is, state is changing inside your component, like in response to a data load or something like that. And we can see it here. The code output here is actually very helpful. It says, inside this use effect, after we get the widgets, we set the widgets in the response.data. And what this is actually getting at, at least in this scenario, is after the test is done, the state is changing in the component. And what that means is, the reason this warning is raised is like, oh, you might think that everything's fine, but then state changes after the end. And your component ends up in a situation that you didn't expect. Maybe we were expecting there to not be widget one on the screen. And so let's do that. Let's actually assert that real quick. Expect screen.getByText.queryByText, to be null. We're saying, hey, when we get widgets, let's make sure that widget one is null. So that passes. So we have widget one is null. We're good to go, but widget one comes in later. It's sneaking in after the fact. And so that's why this warning is here to warn you there is a state change that you are not waiting for, and it might be causing a problem. Generally, the solution in this scenario, at least when you have a load from an effect or something like that, the solution is not to put an act around something. That doesn't actually help. You're not firing an event that updates the state. It's happening automatically as a component. The solution instead is to do the waiting that you're doing before, wait for something on the screen that's going to change, because that is... This is waiting for the state change. But then we phrase this in terms of the contract. You're waiting for the externally visible result that comes from the final state change. So we save. And the warning goes away, because after we've waited for widget one to appear, there are no more state changes. There's no more surprises. And React can see, oh, there's no more surprises coming in, so you can be confident that your test is really testing the final state of the component. So generally... And in going from a React expert that I used to work with, she was really helpful to me in learning about these things. She was like, probably there's a wait. Like even if you're not sure what's happening, probably there's a state change. Sometimes the test output, I find... Usually lately, the warning output has been showing me where the state change happens, but maybe it's mysterious, or maybe it's not outputting correctly. But she really encouraged me. Like, Josh, like there's probably something to wait for. And the other thing to think about is maybe you do have a fire and forget thing that happens and you don't have a user visible response that shows them that it's completed. And if that's the case, maybe you could benefit from that. Maybe you would help the user to see a loading state, as Stella was mentioning, and then a success state at the end. And even something as simple as a little spinner with accessibility props so that blind folks or folks with visual impairments can tell what's going on. But maybe just that little visual indication could actually improve the user experience. Because if the test can tell that everything is done, your user can as well. And that can be useful. That might not always be the case when you have a lot of things under the hood going. And so in some of my code, I can't get rid of all of those warnings. But it's good to try. It's good to heed the warning to see if you can make your tests more reliable. Certainly see if there's any bugs that are slipping through. So that's the ACT warning and what's going on there. With this, let's wrap up the workshop. We're ending a little early so we have time for some questions if folks have it. But let's talk about the final things here. What did we learn? And I'll go ahead and answer these questions. What does it mean to test the contract? Testing the contract means thinking about your components in terms of the inputs and outputs and what the rest of the application code expects. Not necessarily the user because the user only sees the outside of your entire application. But the other components that are using this component. What are they going to give this component as input or the user is going to give the component as input. And what outputs do we see? We saw the contract in terms of inputs as props and inputs as user interactions. And the outputs in terms of rendered elements to the screen and in terms of functions that are called to send data elsewhere or just a call. Why test the contract? That's something we talked about as well.

23. Conclusion and Further Learning

Short description:

Testing the contract tends to result in more durable component tests that only change when the contract changes. It supports refactoring, rearranging, and changing the look and feel of the component. Different situations require different testing approaches, so it's important to try different strategies and see what works best. Continuing the learning, visit the workshop landing page on rntest.org for resources and links to the React Native Testing Library docs. Join the Discord community and livestream to engage in discussions about React and React Native testing. Test Double is available to help with testing and application design, and the speaker encourages reaching out for collaboration. The speaker expresses gratitude to the employer for the opportunity to conduct the workshop and visit England. Contact information and resources are provided for further questions and feedback.

And I maybe if I didn't give as much detail before let me give a little bit more. If you're testing just your whole application that is the most realistic from a user standpoint. But that will A interact native is a bit harder because native testing facilities for the full application are harder than on the web. And also you're probably not gonna get down to all the edge cases to test every loading state and every error state and then end to end test. And if you do your tests are probably gonna be very slow. Instead, it can be beneficial to have those high level end to end tests cover just the main pathways and have component tests cover more focused places. I have one React component that I'm building on my client project right now that has dozens and dozens of different states. I do not want to run through the whole application to test all of those. I want to test that component directly, give it the data it needs and check the output. It's much more focused, much clearer and easier to understand. So those are reasons to test the contract. Another reason is that your tests can be more robust. Think of the flip side. If you're going in and just testing all the details of the component, if you're getting into the internal state or internal function calls, which I would agree are implementation details. Or I would say, even if you're testing as snapshot output, I would say that generally is getting into implementation details because it's checking every little detail of JSX that's outputted, not just the parts that you care about, that the application cares about and the contract. So testing the contract tends to result in component tests that are more durable. They tend to only change when the contract changes. They support your refactoring the component, rearranging it, even changing the look and feel of it. And those ended being tests that add more value and have less costs over time. And then I already said whether the inputs and outputs of the component just a moment ago. So that has been the focus of our workshop. This is an approach to testing that has served me really well and served folks that I've been on teams with well. And I pass it along to you and encourage you to check it out as well. As I said, we all need to make our own decisions as developers and different situations are different. And so I would encourage you to try these things, see what works for you, see what you think differently about, and let me know. I would love for you to get in touch and reach out and tell me what you do differently. That would be really great. Let me come back to remaining questions in just a second. But I want to first of all, keep an eye on the chat and also, yeah. Come back to that. I wanna share these links and I'll leave these up on the screen and put these in the Discord chat. So continuing the learning, this is just an introduction to React Native Testing Library. And to learn this library and the React Native ecosystem and just test design in general, there's always more to learn. So places you can do that are, go to the workshop landing page on rntest.org. This page will be up indefinitely with lots of different resources, of things you can learn, links to the React Native Testing Library docs. I'm gonna add those links about accessibility there and the links to Kent C. Dodd's article about the ACT warning. I don't think it's already on there, so I'll add those resources as well. You can also continue learning in my Discord Community. So that's linked from that page. It's a community of people that talk about React and React Native, especially about testing them. And if you join in on my livestream, this is a place where people who are on there or have been on there will chat as well. So check out that link, would love to have you continue the conversation. Again, a test double would love to talk to you as well. I wanna thank my employer for giving me a chance to come and do a workshop for you and to come and visit England. It's been so cool to be here. So test, I would love to talk to you. If you want someone to think about testing this way or to challenge and to say, no, that way of testing doesn't work, here's what we're trying but here's what we're struggling with. We would love to join your team, understand your specific pain points for testing or just application design or anything and work on improving it together. We'd love to join teams and work on solutions together. So reach out to test double if we can help you. I would love to join where some of my coworkers who are really great and I learned from would love to join you sometime soon. So thanks so much. Yeah, I will keep the slide up so you can get in touch. There's the workshop landing page there. There's my test double email address. You can reach me there with any questions. That's also listed on the workshop page. But yes, anyone is free to go because we're just going to be discussing questions here for a few minutes if anybody has any, but yeah, what questions or feedback do you have about what we covered? Anything that you wanted to get to about testing that we didn't get to today? Hang on for a few minutes if anybody has any questions.

Watch more workshops on topic

React Summit 2022React Summit 2022
118 min
Detox 101: How to write stable end-to-end tests for your React Native application
Workshop Free
Compared to unit testing, end-to-end testing aims to interact with your application just like a real user. And as we all know it can be pretty challenging. Especially when we talk about Mobile applications.
Tests rely on many conditions and are considered to be slow and flaky. On the other hand - end-to-end tests can give the greatest confidence that your app is working. And if done right - can become an amazing tool for boosting developer velocity.
Detox is a gray-box end-to-end testing framework for mobile apps. Developed by Wix to solve the problem of slowness and flakiness and
used by React Native itself
as its E2E testing tool.
Join me on this workshop to learn how to make your mobile end-to-end tests with Detox rock.
Prerequisites
- iOS/Android: MacOS Catalina or newer
- Android only: Linux
-
Install before the workshop
React Advanced Conference 2022React Advanced Conference 2022
81 min
Introducing FlashList: Let's build a performant React Native list all together
Workshop Free
In this workshop you’ll learn why we created FlashList at Shopify and how you can use it in your code today. We will show you how to take a list that is not performant in FlatList and make it performant using FlashList with minimum effort. We will use tools like Flipper, our own benchmarking code, and teach you how the FlashList API can cover more complex use cases and still keep a top-notch performance.
You will know:
- Quick presentation about what FlashList, why we built, etc.
- Migrating from FlatList to FlashList
- Teaching how to write a performant list
- Utilizing the tools provided by FlashList library (mainly the useBenchmark hook)
- Using the Flipper plugins (flame graph, our lists profiler, UI
&
JS FPS profiler, etc.)
- Optimizing performance of FlashList by using more advanced props like `getType`
- 5-6 sample tasks where we’ll uncover and fix issues together
- Q
&
A with Shopify team
React Summit Remote Edition 2021React Summit Remote Edition 2021
60 min
How to Build an Interactive “Wheel of Fortune” Animation with React Native
Workshop
- Intro - Cleo
&
our mission
- What we want to build, how it fits into our product
&
purpose, run through designs
- Getting started with environment set up
&
“hello world”
- Intro to React Native Animation
- Step 1: Spinning the wheel on a button press
- Step 2: Dragging the wheel to give it velocity
- Step 3: Adding friction to the wheel to slow it down
- Step 4 (stretch): Adding haptics for an immersive feel


React Summit 2023React Summit 2023
88 min
Deploying React Native Apps in the Cloud
Workshop Free
Deploying React Native apps manually on a local machine can be complex. The differences between Android and iOS require developers to use specific tools and processes for each platform, including hardware requirements for iOS. Manual deployments also make it difficult to manage signing credentials, environment configurations, track releases, and to collaborate as a team.
Appflow is the cloud mobile DevOps platform built by Ionic. Using a service like Appflow to build React Native apps not only provides access to powerful computing resources, it can simplify the deployment process by providing a centralized environment for managing and distributing your app to multiple platforms. This can save time and resources, enable collaboration, as well as improve the overall reliability and scalability of an app.
In this workshop, you’ll deploy a React Native application for delivery to Android and iOS test devices using Appflow. You’ll also learn the steps for publishing to Google Play and Apple App Stores. No previous experience with deploying native applications is required, and you’ll come away with a deeper understanding of the mobile deployment process and best practices for how to use a cloud mobile DevOps platform to ship quickly at scale.
Vue.js London Live 2021Vue.js London Live 2021
89 min
Building for Web and Native with Ionic & Vue
Workshop
When building an app, there are many options choices developers need to make. Is it a web app? Does need to be a native app? What should I use for UI? In this workshop will look at how to make use of Ionic for building your app and how to deploy it to not only the web, but native as well.


React Advanced Conference 2021React Advanced Conference 2021
90 min
Building for Web and Native with Ionic & React
Workshop
When building an app, there are many options choices developers need to make. Is it a web app? Does need to be a native app? What should I use for UI? In this workshop will look at how to make use of Ionic for building your app and how to deploy it to not only the web, but native as well.


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 Finland 2021React Finland 2021
27 min
Opensource Documentation—Tales from React and React Native
Documentation is often your community's first point of contact with your project and their daily companion at work. So why is documentation the last thing that gets done, and how can we do it better? This talk shares how important documentation is for React and React Native and how you can invest in or contribute to making your favourite project's docs to build a thriving community


React Advanced Conference 2021React Advanced Conference 2021
21 min
Building Cross-Platform Component Libraries for Web and Native with React
Building products for multiple platforms such as web and mobile often requires separate code-based despite most of the components being identical in look and feel. Is there a way where we could use shared React component library on different platforms and save time? In this presentation I'll demonstrate one way to build truly cross-platform component library with a unique approach of using React
&
React Native in combination.


React Advanced Conference 2021React Advanced Conference 2021
27 min
Limitless App Development with Expo and React Native
App development is hard, React and Expo make it easy!
It's never been simpler to build and deploy powerful mobile apps with incredible features to both Android and iOS users all over the world.
We’ll discuss building and deploying mobile apps seamlessly from the cloud using EAS, creating powerful dev clients (like browsers but for mobile app development) for testing your app, pushing OTA updates instantly to users, and much more — no native experience required!