Tutorials on Testing

Learn about Testing from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

Cypress Studio - the underrated feature speeding up e2e testing

Testing is basically a requirement for modern software today, not a nice-to-have. In the past, end-to-end testing was hard to set up, flaky, and generally a pain to deal with, but it's the best automated testing option to confirm software works. Cypress.io continues to improve the e2e testing experience and its new feature Cypress Studio takes it a step further to make writing tests quicker and easier too.Photo by Farzad Nazifi on Unsplash When Cypress.io first hit the scene in 2015, it made a splash because it fixed so many of the issues that existed with other end-to-end testing (e2e) competitor frameworks. Between good documentation, intuitive syntax, improved debugging, and no reliance on Selenium under-the-hood - everything about Cypress was a major step forward for e2es, but it wasn't content just to stop there. The team behind Cypress regularly keeps releasing new features and functionality to make it more and more useful for devs, and make e2e testing (traditionally kind of a pain) easier and easier as well. One recent release that's currently tucked behind a feature flag is called Cypress Studio , and it's an absolute game changer. Today, I'll show you how to add Cypress to an existing JavaScript project, enable Cypress Studio, and let it help do the heavy lifting of writing end-to-end tests. It's such a cool feature to help dev teams save time on testing, deliver new features faster, and still ensure the mission critical functionality of the app continues to work as expected. Although Cypress is kind enough to provide a host of sample scripts to show many of its features in action, it really shines with a local app to test against, and I just so happen to have one that fits the bill. The app I'm using is a React-based movie database that allows users to see upcoming movies and movies in theaters now, browse movies by genre, and search for movies by title. This will be a good app to demonstrate Cypress Studio's power. Once we've got an app to add Cypress to, the first thing we'll need to do is download Cypress to it. This is another reason Cypress stands head and shoulders above its competitors: one npm download gives you all the tools you need to start writing e2es. No dev dependencies, no extra libraries with mismatched package versions, none of that nonsense to deal with. At the root of your project, where your package.json  file lives, run the following command from the terminal: This will add a bunch of new Cypress-based folders and files to your app, and with just a few small configuration changes we'll be ready to go. See all those new folders under cypress/ ? That's what you should see after initial installation. For ease of use, I like to add npm scripts for the two main Cypress commands we'll be using: In your package.json  file, add the following two lines in your "scripts"  section. Now when we need to run the tests, a simple npm run cy:run  or npm run cy:open , straight from the command line, will do the trick.  Ok, before we get to writing our own tests, let's run the pre-populated tests in Cypress to get familiar with the its Test Runner. From your command line, run the following shell command: This should open the Cypress Test Runner, and from here, click the Run integration spec button in the top right hand corner to run through all the pre-made tests once. After all the Cypress tests run and pass, we're ready to delete them and get to work on our own tests for our app. Go ahead and clear all the files out of the Cypress folders of fixtures/ and  integration/ . You'll probably also want to add the folders of cypress/screenshots/  and cypress/videos/  to your .gitignore  file just so you don't commit those screenshots and videos that Cypress automatically takes during test runs to your GitHub repo (unless you want to, of course). Add baseUrl variable With that taken care of, let's set up a baseUrl  in our cypress.json file and enable Cypress Studio there too. Turn on experimentalStudio To enable Cypress studio, just add "experimentalStudio": true  to our cypress.json  file. So here's what the cypress.json  file will end up with. Now we can write our first test file and test. Inside of the cypress/integration/  folder in your project, create a new test file named movie-search-spec.js . This folder is where Cypress will look for all your e2e test files when it runs. Give it a placeholder test: we have to tell Cypress where we want it to record the test steps we're going to show it. So just create a typical describe  test block and inside of that, create an it  test. A good first test would be to test that a user can navigate to the movie search option, search for a particular movie name, and click into the results based on that search. Here's what my empty testing placeholder looks like in the movie-search-spec.js  file. I think we're about ready to go. One thing you must do before starting up Cypress to run tests against your local app is to start the app locally. For Cypress, it's an anti-pattern to start the app from a test, so just fire it up in a separate terminal, then open up the Cypress Test Runner. Start the movie app In one terminal run our movie app: Start the Cypress Test Runner And in a second terminal, open the Cypress Test Runner: In Cypress, Add Commands to Test When the Cypress Test Runner is open, enter our test file and click the tiny blue magic wand that says  Add Commands to Test  when you hover over it. And from here, go for it - test the app. For me, I clicked the Movie Search link in the nav bar, typed "Star Wars" into the search box, clicked into one of the results, etc. When you're satisfied with what your test is doing, click the Save Commands button at the bottom of the test, and Cypress will run back through all the commands it's just recorded from your actions. Tell me that's not cool. If you go back to your IDE now, you'll see all the actions Cypress recorded, along with a few comments to tell you it was Cypress generating the code and not a developer. This is what my test now looks like: Just wow, right? Although our test is good, Cypress can't be expected to test for all the things a developer might know are important. Things like the number of movies returned from searching "star wars" or checking the title of the movie being clicked into and the contents inside of the movie page itself. I'll fill in some of those details myself now.  If you look at my code above, I added comments after the extra assertions I added - mainly small things like checking for search text, the count of movies returned, the movie info like rating and release date in this specific movie. I didn't add a ton of extra code, just a few extra lines of details. Now run the test again and check the results. And we're done! Congrats - our first Cypress Studio-assisted e2e test is written.  Just repeat these steps for as many end-to-end tests as you need to write and prepare to be amazed at how much time it saves you. In modern software, testing is a critical piece of any solid enterprise application. It helps ensure our app continues to function as expected while adding new functionality, and end-to-end testing is the closest we can get to mimicking exactly how a user would use the app with automation. Cypress broke the mold of what e2e testing frameworks are capable of when it debuted back in 2015, and it's only continued to improve with time. My favorite new feature of late is the ability to show  Cypress how a test should act instead of writing it yourself with Cypress Studio - the time saving possibilities are immense. And more time saved means finishing features faster and getting new functionality into the hands of users quicker. Win win win. In 10 modules and 54 lessons, I cover all the things I learned while at The Home Depot, that go into building and maintaining large, mission-critical React applications - because it's so much more than just making the code work. From tooling and refactoring, to testing and design system libraries, there's a ton of material and hands-on practice here to prepare any React developer to build software that lives up to today's high standards. I hope you'll check it out.

Thumbnail Image of Tutorial Cypress Studio - the underrated  feature speeding up e2e testing

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Testing a Go and chi RESTful API - Route Handlers and Middleware (Part 2)

Disclaimer - If you are unfamiliar with writing a simple unit test for a route handler of a Go and chi RESTful API, then please read this blog post before proceeding on. Go's testing library package provides many utilities for automating the testing of Go code. To write robust tests for Go code, you must already understand how to write a basic Go test suite that contains several TestXxx functions. Writing tests for code, especially within the context of test-driven development (TDD), prioritizes the code's correctness over the code's flexibility to adapt to new/updated requirements. The guarantee of less unexpected regressions offsets the upfront cost of spending more time to write tests alongside application code. In a fast-paced, high-pressure environment, it can be difficult to convince other team members and stakeholders of the value in testing code when time is an extremely limited resource. Another factor that must be considered is the amount of code covered by the tests. If the tests cover only a small percentage of the application code (or a small subset of use cases), then the benefits of having these tests probably won't outweigh the benefits of adding new features or improving existing features. Plus, anytime you decide to refactor the application code, you will also have to update the corresponding tests to reflect these changes. When time is so valuable, the time spent on writing tests could have been spent elsewhere. Therefore, to fully benefit from tests, you must write enough tests such that they cover a large percentage of the application code. If a RESTful API exposes multiple endpoints, then testing a single route handler won't bring much value for the time spent writing it. Testing RESTful APIs built with Go and chi requires testing not only all of the route handlers, but also, the chi /custom middleware handlers. Let's write tests for... Clone a copy of the Go and chi RESTful API (with a basic test for a single endpoint) from GitHub to you machine: This RESTful API specifies five endpoints for performing operations on posts: If you would like to learn how to build this RESTful API, then please visit this blog post . Run the following command to install the project's dependencies: Note : If you run into installation issues, then verify that the version of Go running on your machine is v1.16 . There is a single test within the routes/posts_test.go file: TestGetPostsHandler . It tests the route handler for the GET /posts endpoint, and it mocks out the GetPosts function called by the route handler to avoid sending a network request to the JSONPlaceholder API when executing tests. Compared to testing route handlers for GET requests, testing route handlers for other HTTP methods (e.g., POST and PUT ) involves slightly more code to test requests that carry data in the form of an optional request body: Let's write a test for the POST /posts endpoint. Before starting, we must first review the route handler for this endpoint ( Create ) in the routes/posts.go file. ( routes/posts.go ) To test the route handler Create , we must first mock out the CreatePost function to avoid sending a network request to the JSONPlaceholder API when executing tests. CreatePost accepts a request body and returns an HTTP response and error (if encountered). Therefore, the mock function must follow the same function signature as CreatePost , like so: CreatePost is defined on type JsonPlaceholderMock . The request body must contain the following information: Since this request body (JSON data) must be passed as type io.ReadCloser to CreatePost , it must be read into a buffer, converted into a byte slice and then decoded into a Go struct so the data can be accessed normally. When a POST /posts request is sent to the JSONPlaceholder API, it returns a response that contains the newly created post. This post contains the exact same information as the request body with an additional Id field. This Id field is set to 101 to imply that a new post was added since there is a total of 100 posts, and each post has an Id field set to 1 , 2 , 3 , etc., up to 100 . The JSONPlaceholder API doesn't actually create this new resource, but rather, fakes it. So anytime you send a request to POST /posts , the response returned will have a newly created post with an Id field set to 101 . In our mock function, let's create this dummy data to send back in the response. Encode this struct to JSON via the json.Marshal method and return it within the body of a HTTP response. This HTTP response should return with a 200 status code to indicate a successful request. Note : This HTTP response returned must adhere to the http.Response struct , which accepts an integer value for its StatusCode field and a value of type io.ReadCloser for its Body field. The ioutil.NopCloser method returns a ReadCloser that wraps the Reader (in this case, bytes.NewBuffer(respBody) , which prepares a buffer to read respBody ) with a no-op Close method, which allows the Reader to adhere to the ReadCloser interface. Putting it altogether... With the mock function now implemented, let's write the test for the route handler Create for POST /posts . Start by naming the unit test TestCreatePostHandler , like so... Then, write this test similar to the TestGetPostsHandler test, but with several adjustments to account for it testing a POST route handler: Putting it altogether... To test the chi logger middleware handler ( middleware.Logger ), we must first understand the implementation details of this handler function: ( go-chi/chi/middleware/logger.go ) The middleware.Logger function only calls a package-scoped function, DefaultLogger , which logs information for each incoming request. ( go-chi/chi/middleware/logger.go ) This package-scoped function accepts a handler function and returns a handler function. Therefore, we can write a test that involves passing a simple route handler to middleware.DefaultLogger and calling middleware.DefaultLogger 's ServeHTTP method with a response recorder and created request: Try adding more endpoints and writing tests for those endpoints' route handlers.

How to Test Your First React Hook Using Testing Library

In previous posts we learned: In this post, we're going to write tests for a custom React hook. We're going to create a custom hook that will uppercase a given text string. This hook will be connected to an input and whenever a user changes the input value this hook will automatically uppercase the value. Let's write the hook. Create a file called useUppercase.ts In this file, we create a custom hook function useUppercase() . It accepts a string as an initial argument. We transform this string using .toUpperCase() and then store the result value in a local state. Also, we create a function update() . This function will be a part of the public API. With this function, components will be able to update the current value. Finally, we use useEffect() to re-render the hook if the initialValue gets changed. This, for example, happens when the component gets new props and passes the new initial value to the hook. In this case, we need to update the local state with the new value from the component. Now, let's test the hook! To test hooks, we're going to use React Hooks Testing Library . It gives us utilities for testing hooks without even rendering a single component. Let's install it: Now, we create a file useUppercase.test.ts . In this file, we import our hook and describe our first test: The first thing we want to test is that the hook returns a given initial value in the upper case. With React Hooks Testing Library, we don't need to render a whole component, instead, we render the hook itself with a special function called renderHook() : The renderHook() function returns an object with the result field. We can access the current render result with the current field. It contains the object our hook returns. We can use autosuggestions to select a field to test against: The coolest feature is readability. We don't need to create extra infrastructure code to test hooks in isolation. It just works! Another thing we want to test is that when we call update with a new string, the hook's value updates. Let's write another test: The current render result contains a reference to our update() method, so we can use it to simulate a change. Notice that we need to use act() . It is required for the hook to update the values inside of it. According to the documentation : The last thing to cover is a case when the hook gets a new initialValue . In this case, we need to update its values and re-render the component that is using them. In this test, we access not only the result but also the rerender() method. This method renders the hook with another initial value. Also, this time we use props to pass initial values. They help us to keep the code shorter and more readable. In general, we pass the initialProps object to options of the render() method. When we need to re-render the hook, we can pass updated values for this object. The library infers the type of the object so we can use autosuggestions here as well:

Thumbnail Image of Tutorial How to Test Your First React Hook Using Testing Library

Testing a Go and chi RESTful API - Route Handlers (Part 1)

Testing plays a fundamental role in the development of quality software. Shipping and deploying software with undetected bugs and regressions opens up a can of terrible consequences such as losing the trust of end users or costing the business time and resources. In a large collaborative setting, having developers manually test each and every feature and user flow for bugs and regressions wastes valuable time that can be put towards improving other aspects of the software. As the codebase and team grows, this approach will not scale. By writing unit/integration/end-to-end tests, identifying and catching bugs and regressions throughout an entire codebase becomes a painless, automatable task that can easily be integrated into any continuous integration pipeline. Unlike most other languages, the Go programming language provides a built-in, standard library package for testing: testing . The testing package offers many utilities for automating the testing of Go source files. To write a test in Go, define a function with a name prefixed with Test (followed by a capitalized segment of text) and accepts an argument of struct type T , which contains methods for failing and skipping tests, running multiple tests in parallel, formatting test logs, etc. Example : This test checks whether the Sum function correctly calculates the sum of two integer numbers. If the sum does not match the expected value, then the test logs an error message and marks itself as having failed. Try it out in the Go Playground here . Below, I'm going to show you: Clone a copy of the Go and chi RESTful API from GitHub to your machine: This RESTful API specifies five endpoints for performing operations on posts: If you would like to learn how to build this RESTful API, then please visit this blog post . In the testless branch's version of the RESTful API, the posts.go file has been moved to a routes subdirectory, and the route handlers within this file have been refactored into a routes package to allow the testing of packages independent of the main package. Run the following command to install the project's dependencies: Note : If you run into installation issues, then verify that the version of Go running on your machine is v1.16 . To get started, let's create a posts_test.go file within the routes subdirectory. This file will contain tests for each of the route handlers within the posts.go file. Inside of posts.go , each endpoint is defined on the chi router via a corresponding routing method named as an HTTP method. To register the GET /posts endpoint on this router, call the .Get method with the route / (in main.go , this posts sub-router is attached to the main router along the /posts route). ( routes/posts.go ) rs.List ( rs refers to the PostsResource{} struct) is a route handler method defined on the PostsResource{} struct. It retrieves a list of posts from the JSONPlaceholder API: ( routes/posts.go ) Let's write a unit test , which tests a single unit of code (commonly a function), for the PostsResource{}.List route handler function. Start off with a simple failing test that will immediately fail and print the message "Not yet implemented." to the terminal: ( routes/posts_test.go ) To test all packages within the current working directory recursively... To test the route handler for GET /posts : Let's combine these code snippets together: ( routes/posts_test.go ) Run the test: Congrats! The route handler test passes! Notice how the TestGetPostsHandler takes approximately half a second to run due to the network request the PostsResource{}.List route handler sent to the JSONPlaceholder API. If the JSONPlaceholder API experiences heavy traffic or any server outages/downtime, then the network request may either take too long to send back a response or completely time out. Because our tests rely on the status of a third-party service, which we have no control over, our tests take much longer to finish running. Let's work on eliminating this unreliability factor from TestGetPostsHandler . PostsResource{}.List calls the GetPosts function, which sends this network request and pipes the response back into PostsResource{}.List if there was no network request error encountered. ( routes/posts.go ) Since the function responsible for sending the network request ( GetPosts ) is a package-scoped variable within the routes package, this function can be replaced with a mock function , which replaces the actual implementation of a function with one that simulates its behavior. Particularly, the network request will be simulated. As long as the mock function has the same function signature as the original function, calling the route handler in a test will remain the same. Inside of posts_test.go , add a mock function for GetPosts : ( posts_test.go ) This mock function creates some dummy data ( mockedPosts , which is a list containing one post), encodes this data as JSON via json.Marshal and returns a minimal HTTP response with a status code and body. These fields adhere to the http package's Response struct. At the top of the TestGetPostsHandler test, set the GetPosts package-scoped variable to this mock function and change the expectedTotal to 1 : Run the test: Wow! The mock allows our test to run much faster. Click here for a final version of the route handler unit test. Try writing tests for the other route handlers.

Thumbnail Image of Tutorial Testing a Go and chi RESTful API - Route Handlers (Part 1)

How to Write Your First Component Test in React + TypeScript App

In the previous post , we created a unit test for a function. In this post, we're going to create a unit test for a component using @testing-library/react . Since we're using Create React App the React Testing Library is already installed in our dependencies. If you don't have it, install the package using: We will create a Greetings component with the greetings text inside and a button for sending friendly waves 😊 Let's create and review a component first: This component takes a name and an onSendWaves function as props. The name will be rendered in the greetings text, and the callback function will be called whenever the button is pressed. The button can be hidden if the callback function is not provided. We need to test 3 things here: Let's start with the first one. For the first test, let's check if the greetings text is rendered correctly. Let's break the test code down a bit. In describe and it we explain the assumption we want to test as we did in the previous post . Then, we use the render method from the @testing-library/react package. This method renders the component provided in arguments and appends it to the body of the document. Once we're rendered the component we can use the screen object to get access to the render result. It provides a set of queries for us to find the required element. If you saw tests with RTL before, you might have seen this: We use getByText query to find an element that contains the given text. Notice that React Testing Library doesn't focus on the component implementation. We don't explicitly define where to look for that text, instead, we tell what to find and RTL will try to find it for us. Finally, we check that the required element is indeed in the document. We can select elements not only by the text they contain. In fact, there are quite a few ways to query elements . Let's review a couple of types of queries : Another cool (and a bit confusing at first) feature is the query type. Until now we only saw a getBy query. It searches for a single element and returns it if found and throws an error if didn't find. There are also other types of queries for different types of searches : The queryBy and queryAllBy are usually used to make sure that elements are not in the document. The findBy and findAllBy are used to search for elements that are not available at first render. It is hard to pick a query at first, that's why there is a priority guideline in the docs and the cheatsheet to help us. Now, let's test that when the onSendWaves is provided the button gets rendered: Here, we use getByRole query because we need to find a single button element. React Testing Library has the fireEvent object that can help with simulating browser events, however, it is more convenient to use the user-event library since it provides more advanced simulation of browser interactions. Let's install it with: Now let's test that when the button is pressed it fires the callback: First of all, we use jest.fn method to create a mock function . This mock will help us to test how many times and with what arguments this function has been called. On the last line, we check that the mock instance ( onSendWavesMock ) is called at least once with a given text. Another text will result in the failing test. To click the button we use the userEvent object from React Testing Library. It provides most common actions like click, input changes, and so on. In our case, we need to test the click action, so we use the click method and pass the element that should be pressed. In the next post, we will test react hooks with @testing-library/react-hook .

How to Write Your First Unit Test in React + TypeScript App

Tests make sure that changes in code don't break its functionality. For testing an app, we need a test runner. Create React App provides us a test runner called jest . Let's create a new app and inspect what it contains. The src directory contains all the app code and a file called App.test.tsx . This file contains a test that makes sure that the App component renders a link: Let's break it down and write our own tests to understand what's going on. Every test is a statement that claims something. In our case, test in App.test.tsx claims that the App component should render a “Learn React” link. This kind of statements consist of 3 stages: This is true for the test in the App.test.tsx , let's check: This is considered a good practice to structure tests in this way. Tests that comply the Arrange-Act-Assert structure are clean and easy to read. Let's start with a test for good ol' functions, and then come back to testing components. Testing functions is a bit easier to grasp and it will be much easier to apply this knowledge later, when testing components. Open App.tsx and add a new function: Now, open the App.test.tsx file and start testing this divide function. First of all, we need to describe what we want to test. For descriptions, jest has a describe function , which we can use like this: We can indent describe functions to create describe specific details about current tests we write: For actually running tests jest has it and test functions . We can use either of those, they do the same. The only difference is the name semantics: we can use it to construct test sentences more naturally. For example: Finally, let's actually write our first test! In the Assert stage we used a function called expect . It is also a jest global function . It returns an object with a bunch of useful methods to use. The full list of them we can find in the docs . In our case we use .toEqual method to check if the expect argument is equal to toEqual argument, i.e. if the expected result is equal to actual result. Now, it is time to check if our test is working, open the console and run: You will see that all the tests are passing: This is great! But we have to break our test 🤯 The immediately appearing thought is: “What would we do that?”. The reason is that we need to make sure that our test is working and that it tests the right thing . Let's look at the example: So, to test our test we're going to break it and see if it breaks with the correct intent. In this case we will see an error output: If we examine the reason why the test has failed, we will see that expected value is not equal to actual. This reason is valid, so the works correctly. Now let's try to test that divide function doesn't allow to divide by 0. We know that if we try to do that it will throw. To check functions that may throw we will use .toThrow method . Notice that we pass a () => divide(1, 0) here, this way jest knows that the occurred error is expected and there is no need to stop the testing itself. Again, let's test our test: The output will be: Thus, we know that our tests are correct. Later, we will use jest for writing tests for React component.

Thumbnail Image of Tutorial How to Write Your First Unit Test in React + TypeScript App

Testing Custom React Hooks with Jest

Hooks in React are a new, popular, and extensible way to organize side-effects and statefulness in React components. By composing the base hooks provided by React, developers can build their own custom hooks for use by others. Redux, ApolloClient, and Callstack have distributed custom hooks to access your app's store, client, and themes using the useContext hook. You can also compose useEffect and useState to wrap API requests or wrap the concept of time. It's powerful. It's simple. These two statements are not a coincidence, either: the power is the simplicity. The same goes for testing custom React hooks. By the end of this write-up, you'll think testing hooks as simple, too. We'll learn about React hooks, React in general, some basics of unit testing, and the bigger picture when it comes to automated testing. Don't panic, it's all right here in GitHub . Give the repo a star before cloning. React hooks are not quite normal functions. They need to run in a React render context, otherwise, they will give you an annoying error. Other literature on this topic suggests building a React component in the test and hacking that to test your hook's functionality using component testing tools like Enzyme. I found this approach to be brittle (who wants to maintain an unused component for every hook change?) and unnecessary. Try using the react-hooks-testing-library . It makes testing React hook behavior, parameters, and return values a breeze. Much easier than dealing with Enzyme, for example. Key concepts are the renderHook and act utilities. The first is where to specify your React Hook, context, and parameters. For instance, here is how to set up and test a React hook's parameters and return value. The act utility is for triggering side-effects for your hook to respond, like events or changing props. It is the same as the act provided by React . We'll see it in action later when we learn (spoiler alert) time control. Mocking is like the beginning of an Indiana Jones movie. It's like filling a bag with sand so when we swap the sandbag with the treasure, the detector doesn't trigger the trap. The detector has to not see the difference between the mock and the real thing. For example, you can mock axios so your hook does all the steps to send an HTTP request without actually sending a request. Plus, because you control the mock of axios , you can decide when the promise resolves or rejects. In React, dependencies can be your package.json dependencies or the exports of other source modules through import/requires, provided through context, or defined elsewhere in the file. If your hook depends on it in order to work, then it's a depend ency. When it comes to testing anything, from a web application to - yes - a React hook, there are different levels at which we can test. The core question of automated testing is this: how much should we mock? If we mock too many dependencies, we lose confidence that the system works as a whole. However, if we mock too few, the sheer number of moving parts cause our tests to be slower, less reliable, and harder to debug/pinpoint failures. If you want to learn more about managing software quality and automated testing, check out these 2014 slides from Google engineering. The prevailing wisdom is to have more small tests with mocks and a few large and valuable end-to-end tests. This is often visualized using a pyramid, called the "testing pyramid". In essence, most of your tests should mock a lot (unit tests), then some tests should mock less and ensure lower-level units are wired up together properly (integration tests), and then fewer tests mock even less and ensure the mid-size units interact properly (also integration tests). At the top of the pyramid, tests should have no mocks yet be very few in number (end-to-end tests). To use another analogy: if you were testing a car, you would test if the sparkplug works, then make sure the assembled piston works after testing its parts, then make sure the engine works after testing its parts, then make sure the whole car starts and survives a lap around the track after testing the gearbox, chassis, brakes, etc. Your hooks rely on dependencies to get almost anything done. Maybe your hook reads state from a Redux store, or perhaps your hook triggers HTTP requests or GraphQL queries. When unit testing React hooks, you want to avoid depending on anything outside of your UI code, like backend or browser APIs. That way, your tests failing means the problem is with the UI and not somewhere else. Test frameworks give the ability to create mock functions. I find the Sinon.js documentation for stubs (same idea, different terminology) does a nice job teaching this concept and showing examples. In Jest, the key is jest.fn() or jest.spyOn() for mocking methods. For our purposes, mocking allows us to fashion a fake function or object without calling the true implementation, avoiding unwanted side-effects. For example, if I have a class controlling an alert noise in my application, I can call jest.spyOn(NoiseService, 'playLoudHorn') to have that function call, when tested, not try to make a real noise. If the method is supposed to return a boolean to indicate success/failure, you can do .mockReturnValue(true) on the end of a mock to have the mocked function return true for this test. The hard part about stubbing/mocking is getting access to the dependency so we can spyOn it or so we can replace it entirely with a mock. The rest of this article is about several ways to inject our mocks into a React hook. In pure functions, all dependencies are passed as arguments to the function. The benefit of this approach is it makes providing (injecting) dependencies during tests straight-forward. Outside of testing, if the dependencies are given default values, the original functionality can be preserved. In this useTime() hook ( read the Medium article for implementation details ), a _getTime parameter is exposed to allow specifying a different function to get the current time. ( I used Luxon for handling DateTimes , I like it more than momentjs) Suppose I want to control what time my useTime() hook thinks it currently is. Instead of mocking the Date object or spying on DateTime.local , I can simply pass in a mock function as _getTime . Here's a test where we do exactly that: (For this test, the actual return value of _getTime is not critical, so I used a string) Of course, by specifying a default function, I can avoid specifying the getTime option when consuming the hook in my components. Here's a sample usage of the useTime hook, without any _getTime specified. An alternative way to control/spy/mock an import is to use the Jest module mocking tools (or tools like proxyquire ) to inject mocks through the module system. Just specify the exact string used for require-ing the dependency, then provide your own mock before importing the unit under test. While this example works, I find with larger test files it can be prone to mutation by tests and can, as a result of mutation behavior, become more complicated. Tests should be easy. Dependencies in React can be provided through the React Context API. This is how components connected to Redux store are able to access state, for example. Let's look at a trivial hook which accesses a context to build a URL string based on a configuration object. We're going to test useResumeURL() . Here's a sample usage: In our test, we can rely on default context, or we can specify a new context using the wrapper option from @testing-library/react-hooks to loop in the context's Provider to inject a mock context value. Let's do both: Made a quick testing factory to build wrappers, so future tests would not be tied to a specific set of values. Keeps tests independent, which is a best practice. Some dependencies can be controlled in unconventional ways. setTimeout and setInterval (generally, the concept of time) can be controlled using Jest ( and Sinon.js, used in this example ). Through experimentation, I found the passage of time has to occur inside act callbacks for the hook to register the effects properly. Another common unconventional dependency mock wraps the XMLHttpRequest (XHR) object so you can block and assert on outgoing network requests, plus control the responses and test the resulting behavior (very nice for testing uncommon API failure scenarios).

Thumbnail Image of Tutorial Testing Custom React Hooks with Jest