Take Testing Further

Both Jest and React Testing Library are very powerful tools: they let you simulate just about anything you want.

Most of the content for the Shiny app comes from data that you get from an API (as we will be getting our content from a CMS). API calls are no exception to the rule. The data can be simulated in testsĀ known as mocks. This meansĀ you can also test other components. šŸš€

Test Your Components That Make API Calls

Install mswĀ  to Mock Your API CallsĀ 

You will need to do some configuration to be able to simulate API calls. Don't worry if you're not a fan of config,Ā it won’t take long!

React Testing Library recommends using an external library: MSW (Mock Service Worker) to make mocks. It's hosted in GitHub. Start by installing the library:

yarn add msw --dev

The Ā mswĀ  library will intercept the API calls that your components make during testsĀ and mocking out what would have been returnedĀ without your app ever knowing what’s going on. Believe me, it’s šŸ”„.

Create Your Mocks

To do this, you need to configure a ā€œserverā€ that will handle the interception of API calls in each of your test files. Start by testing a component on theĀ  Ā Freelancers/index.jsxĀ  page. Then create a file in Ā /pages/FreelancersĀ and call itĀ Ā index.test.jsĀ  .

You’re going to need Ā restĀ  from Ā mswĀ , so do the following:

import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { render, waitFor, screen } from '@testing-library/react'

import Freelancers from './'

const server = setupServer(
   // Specify the url that we want to "intercept"
   rest.get('http://localhost:8000/freelances', (req, res, ctx) => {
      // Here we can pass the mocked data into what is returned in json
         return res(ctx.json({}))
   })
)

// Activate the API mock before the tests from server
beforeAll(() => server.listen())
// Reset anything we might have added in terms of duration for our tests before each test
afterEach(() => server.resetHandlers())
// Close the API mock once tests are over
afterAll(() => server.close())

And that’s all the configuration you need! ✨

Where’s the data that we return?!

Well spotted! It's not in there yet. You need a format that matches what the API returns. If you do a Ā console.logĀ  of whathttp://localhost:8000/freelancesĀ  returns, you’ll see that it’s a list of objects. So create a list of objects for your mock:Ā 

const freelancersMockedData = [
   {
      name: 'Harry Potter',
      job: 'Frontend wizard',
      picture: '',
   },
   {
      name: 'Hermione Granger',
      job: 'Fullstack witch',
      picture: '',
   },
]

Ā ItĀ returns the following in your mock:

const server = setupServer(
   // Specify the url that we want to "intercept"
   rest.get('http://localhost:8000/freelances', (req, res, ctx) => {
      // Here we can pass the mocked data into what is returned in json
      return res(ctx.json({ freelancersList: freelancersMockedData }))
   })
)

You’re all set, so now it’s time to use it! šŸ”„

Use Your Mocks

Now that the configuration is complete, let's use it to test Ā pages/Freelancers/index.jsxĀ  .

But before testing, consider your testing strategy. The component displays a loader while it makes the API request, then displays the data in the Ā CardsĀ  components. So to start with, you can check that:Ā 

1- The loader displays correctly during the call.

2- The firstĀ cardĀ  correctly displays the elements fetched in the call with the first element.Ā 

Start with the first stage. You know that Ā isLoadingĀ  is set to Ā trueĀ  , so you can check that theĀ Ā LoaderĀ  appears as it should.

How? TheĀ Ā LoaderĀ  is a simple styled Ā divĀ and doesn’t contain any text!

The time has come to use data-testid(remember from the last chapter?). To use it, specify this in Ā Freelancers/index.jsxĀ  :

<Loader theme={theme} data-testid="loader" />
```
And in our test, we get it with :
```
test('Should render without crash', async () => {
   render(
      <ThemeProvider>
         <Freelancers />
      </ThemeProvider>
)
   expect(screen.getByTestId('loader')).toBeTruthy()
})

Ā Your test is running. It works. āœ…

How do I know that my test isn’t always right? That it'll just work whatever happens?

Try changing the Ā idĀ  to check that it breaks properly:

expect(screen.getByTestId('thisIdDoesNotMatchAnything')).toBeTruthy()

And the test fails.

To test with your data, you’ll need Ā waitForĀ  , which you already imported from ' Ā @testing-library/reactĀ  ' . This method manages asynchronous code, like with an API call, for example. šŸ˜‰ Don’t forget to also add Ā anĀ asyncĀ before the test callback.Ā 

To check that the code correctly displays the namesĀ Harry Potter and Hermione Granger, you have:

it('Should display freelancers names', async () => {
   render(
      <ThemeProvider>
         <Freelancers />
      </ThemeProvider>
   )
   expect(screen.getByTestId('loader')).toBeTruthy()
   await waitFor(() => {
      expect(screen.getByText('Harry Potter')).toBeTruthy()
      expect(screen.getByText('Hermione Granger')).toBeTruthy()
   })
})

Once again, you can change the text, like this:Ā 

expect(screen.getByText('Harry PotOfButter')).toBeTruthy()

And the test fails! šŸ”„

You can see another example of mocks and a testing strategies in the screencast below šŸ‘‡:

Customize Your render

So far, you've used your Ā ThemeĀ  directly in your tests. That’s not very clean, especially if you’re testing other components likeĀ  Ā HomeĀ  when you’ll also have to wrap it in the router.

But that’s no problem because the render function from React Testing Library can take a wrapper as a parameter.

Declare a new React componentĀ ,Wrapper, in the previous test.

function Wrapper({ children }) {
   return <ThemeProvider>{children}</ThemeProvider>
}

And reuse it by passing it as a parameter of your render:

render(<Freelancers />, { wrapper: Wrapper })

Ta-da! šŸŽ‰

You can even turn this into a tool that you reuse in all your tests.Ā 

To do this, create a Ā /testĀ  folder in Ā /utilsĀ  , and put an Ā index.jsĀ  file whereĀ you will putĀ the tool.

Which gives you:

import { render as rtlRender } from '@testing-library/react'
import { ThemeProvider } from '../../utils/context'

function Wrapper({ children }) {
   return <ThemeProvider>{children}</ThemeProvider>
}

export function render(ui) {
   rtlRender(ui, { wrapper: Wrapper })
}

And now all you need to do is import your new Ā renderĀ  in your Ā /pages/Freelancers/index.test.jsĀ  , and delete renderĀ  from imports with ' Ā @testing-library/reactĀ  'Ā  .

It works perfectly! šŸŽ‰

While we’re here, let's deal with theĀ Ā RouterĀ  and Ā SurveyProviderĀ . If you want to explore alternative options (and if you need to run tests that have access to yourĀ  Ā historyĀ  ), look at the React Testing LibraryĀ  documentation.

In our case, we just want the tests to work even when there’s a Ā LinkĀ  in the components. To do so, transform your file to add theĀ Ā RouterĀ  andĀ  Ā SurveyProviderĀ  :

import { render as rtlRender } from '@testing-library/react'
import { ThemeProvider, SurveyProvider } from '../../utils/context'
import { MemoryRouter } from 'react-router-dom'

function Wrapper({ children }) {
   return (
      <MemoryRouter>
         <ThemeProvider>
            <SurveyProvider>{children}</SurveyProvider>
         </ThemeProvider>
      </MemoryRouter>
   )
}

export function render(ui) {
   rtlRender(ui, { wrapper: Wrapper })
}

The test works! ✨

Discover Other Types of Tests

You’ve learned how to create unit tests with Jest and test your components with React Testing Library. Then you tested your interactions and mocked API calls. But remember thatĀ testing is a huge topic. There are many different tools and approaches.Ā 

In the screencast below, you’ll see a demo of another approach that you haven’t seen. šŸ‘‡

As mentioned, end-to-end testing is another powerful tool. To learn more, use theĀ CypressĀ Testing Library.

Like with everything else in JavaScript, test tools are constantly evolving. So always watch for newĀ ones and then read their documentation.Ā Ā 

Give It a Go!

It’s time to put what you’ve learned in this part into practice. You’re going to create tests for Results/index.jsxĀ . You’ll also have to mock the data, like you’ve seen in this chapter.Ā 

As usual, the code for getting started is on branchĀ P3C3-beginĀ with the solution onĀ P3C3-solution.

Let’s Recap!

  • The Ā mswĀ  library helps with mocking API calls from tests, letting you configure a Ā serverĀ  that returns the desired mocked data.Ā 

  • React Testing Library contains tools such as Ā waitForĀ  , allowing you to test your components after API calls.Ā 

  • It is possible to customize the Ā renderĀ  to include the Ā RouterĀ  and the Ā ProvidersĀ  from context.

  • Other types of tests exist, such as end-to-end testing.

Well done! You’ve finished the part of this course on React that deals with testing. šŸ’Ŗ It’s now time to test your knowledge with a quiz. Good luck, and see you soon for the fourth and final part of this course!Ā Ā 

Et si vous obteniez un diplƓme OpenClassrooms ?
  • Formations jusqu’à 100 % financĆ©es
  • Date de dĆ©but flexible
  • Projets professionnalisants
  • Mentorat individuel
Trouvez la formation et le financement faits pour vous