简体   繁体   中英

How to test React component with nested components/elements with async calls?

I created a weather dashboard component in React which consists of a Select Dropdown and based on the values of the Select dropdown, it fetches data, sets states and then renders Charts . Here is the code:

Dashboard.js

function Dashboard() {

  const [label, setLabel] = useState(null) 
  const [labels, setLabels] = useState<String[]>([]) 
  const [data, setData] = useState<Number[]>([])

  const options = [
    { value: 'temperature', label: 'Temperature' },
    { value: 'dewPoint', label: 'Dew Point' },
    { value: 'visibility', label: 'Visibility' }
];

  const handleChange = async (selectedOption: any) => {
    const response = await fetch('http://localhost:3000/getData');
    if (!response.ok) {
        const message = `An error has occured: ${response.status}`;
        throw new Error(message);
    }
    const jsonData = await response.json();
    setLabel(selectedOption.value)
    setLabels(jsonData.map((item: { timestamp: any; }) => item.timestamp))
    if (selectedOption.value === 'temperature') {
      setData(jsonData.map((item: { temperature: any; }) => item.temperature))
    } else if (selectedOption.value === 'dewPoint') {
      setData(jsonData.map((item: { dewPoint: any; }) => item.dewPoint))
    } else {
      setData(jsonData.map((item: { visibility: any; }) => item.visibility))
    }
    
  }

  return (
    <div>
      <div  data-testid = "Select">
      <Select options={options} onChange={handleChange}/>
      </div>
      {label != null && data  != null &&
        <div data-testid= "charts"> 
          <BarChart data-testid = "BarChart" label = {label} labels={labels} selectedData = {data} />
          <RadarChart data-testid = "RadarChart" label = {label} labels={labels} selectedData = {data} />
          <LineChart  data-testid = "LineChart" label = {label} labels={labels} selectedData = {data} />
        </div>
      }
      {
        label == null && data == null &&
        <div data-testid= "no-charts">
          <h1> No data to fetch. </h1>
        </div>
      }
    </div>
    
  );
}

How would I test this component using Jest. I am looking into tutorials and examples, but I come across simple instances where the component being tested just consists of some html elements. Can I use jest.mock() to test this somehow? How would I get it to fetch the data through Jest? Any advice would be appreciated. I feel stuck and I have been at this for hours.

Generally, you'd use something like React Testing Library which allows you to mount a component inside of jest and then make assertions on it via utilities that inspect the DOM output of that component.

As you mention, you also need to deal with that your component makes.network calls. To mock the.network call you could use Jests spyOn functionality on window.fetch where you would provide a fake response such that when fetch is called, it returns your fake response rather than actually making the call.

However, this can be cumbersome. I'd usually make use of something like MSW which has a nice interface for faking.network calls.

Your example is fairly complicated to assert on as you are rendering charts. Actually, as a first test to write for React, this is at a more advanced level. You could decide to assert on every bar element in the graph but usually, you'd say this is overkill as the chart library you are using is probably also well-tested (unless you've implemented these yourself?), you don't need to test it extensively in your own app.

Additionally, typically charts won't render right inside of Jest anyway as it's a fake DOM environment that doesn't actually fully emulate a browser in terms of layout.

So it might be fine to just say "it gets the right data for the right option and renders the charts with the right data when it arrives". Since you probably don't want to assert on the actual SVG chart elements (and probably can't because of what I said about Jest not being a real browser), you could mock the chart components to basically just output the passed-in data to the DOM, to allow you to assert on it. This is a fairly advanced use case. You'd usually avoid mocking actual components and instead assert on the real dom outputted by the real components in most cases.

I don't know what chart lib you are using so my answer is limited because I can't write the necessary mocks. Let me know though if I can help further.

Additionally, I don't know what select lib you are using so I can't write the bits about selecting the options.

import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { fireEvent, render, screen } from '@testing-library/react'
import Dashboard from './Dashboard' // Change to your file/import

const server = setupServer(
  rest.get('http://localhost:3000/getData', (req, res, ctx) => {
    return res(
        ctx.json([ // I made this up cos I'm not a weather expert but you'd give realistic data
            { temperature: 25, dewPoint: 2, visibility: 10 },
            { temperature: 12, dewPoint: 4, visibility: 9 },
            { temperature: 27, dewPoint: 5, visibility: 4 }
        ])
     )
  }),
)


describe('<Dashboard />', () => {

    beforeAll(() => { 
       // TODO: Mock the chart components with a fake react component that passes through `data` but `JSON.stringify`'ed in the HTML output so you can assert on it.
       server.listen() 
    })
    afterEach(() => server.resetHandlers())
    afterAll(() => server.close())

 
   it('renders temperature charts when temperature option is selected', async () => {
      const { findByTestId } = render(<Dashboard />)
      
      // TODO: Get the option for temperature and use `fireEvent.click` to click it.

      const barChart = await findByTestId('BarChart')
      expect(barChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

      const radarChart = await findByTestId('RadarChart')
      expect(radarChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

      const lineChart = await findByTestId('LineChart  ')
      expect(lineChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

   })

})

You'd repeat this for the other options.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM