
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. š
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 --devThe Ā 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 š„.
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! š„
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 š:
renderSo 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! āØ
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.Ā Ā
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.
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!Ā Ā