title: React - how do I unit test an API call in Jest?
body: <p>I have a bunch of API calls that I would like to unit test. As far as I know, unit testing API calls doesn't involve actually making those API calls. As far as I know you would simulate responses of those API calls and then test on the DOM changes however I'm currently struggling to do this. I have the following code:</p>
  
  <p>App.js</p>
  
  <pre><code>function App() {
  
    const [text, setText] = useState("");
  
    function getApiData() {
          fetch('/api')
          .then(res =&gt; res.json())
          .then((result) =&gt; {
            console.log(JSON.stringify(result));
            setText(result); 
          })
        }
  
    return (
      &lt;div className="App"&gt;
        {/* &lt;button data-testid="modalButton" onClick={() =&gt; modalAlter(true)}&gt;Show modal&lt;/button&gt; */}
        &lt;button data-testid="apiCall" onClick={() =&gt; getApiData()}&gt;Make API call&lt;/button&gt;
        &lt;p data-testid="ptag"&gt;{text}&lt;/p&gt;
      &lt;/div&gt;
    );
  }
  
  export default App;
  </code></pre>
  
  <p>App.test.js</p>
  
  <pre><code>it('expect api call to change ptag', async () =&gt; {
    const fakeUserResponse = {'data': 'response'};
    var {getByTestId} = render(&lt;App /&gt;)
    var apiFunc = jest.spyOn(global, 'getApiData').mockImplementationOnce(() =&gt; {
      return Promise.resolve({
        json: () =&gt; Promise.resolve(fakeUserResponse)
      })
    })
  
  
    fireEvent.click(getByTestId("apiCall"))
    const text = await getByTestId("ptag")
    expect(text).toHaveTextContent(fakeUserResponse['data'])
  })
  </code></pre>
  
  <p>I'm trying to mock the result of getApiData() here and then test a DOM change (the p tag changes to the result). The above code gives me the error:</p>
  
  <blockquote>
    <p>Cannot spy the getApiData property because it is not a function; undefined given instead</p>
  </blockquote>
  
  <p>How do I access that class function?</p>
  
  <p>EDIT:</p>
  
  <p>I've adapted the code but I'm still having a bit of trouble:</p>
  
  <p>App.js</p>
  
  <pre><code>function App() {
  
    const [text, setText] = useState("");
  
    async function getApiData() {
          let result = await API.apiCall()
          console.log("in react side " + result)
          setText(result['data'])
        }
  
    return (
      &lt;div className="App"&gt;
        {/* &lt;button data-testid="modalButton" onClick={() =&gt; modalAlter(true)}&gt;Show modal&lt;/button&gt; */}
        &lt;button data-testid="apiCall" onClick={() =&gt; getApiData()}&gt;Make API call&lt;/button&gt;
        &lt;p data-testid="ptag"&gt;{text}&lt;/p&gt;
      &lt;/div&gt;
    );
  }
  
  export default App;
  </code></pre>
  
  <p>apiController.js</p>
  
  <pre><code>export const API = {
      apiCall() {
          return fetch('/api')
          .then(res =&gt; res.json())
      }
  }
  </code></pre>
  
  <p>Server.js</p>
  
  <pre><code>const express = require('express')
  const app = express()
  const https = require('https')
  const port = 5000
  
  app.get('/api', (request, res) =&gt; {
      res.json("response")
  })
  
  app.listen(port, () =&gt; console.log(`Example app listening at http://localhost:${port}`))
  </code></pre>
  
  <p>App.test.js</p>
  
  <pre><code>import React from 'react';
  import { render, shallow, fireEvent } from '@testing-library/react';
  import App from './App';
  import {API} from './apiController'
  //import shallow from 'enzyme'
  
  it('api call returns a string', async () =&gt; {
    const fakeUserResponse = {'data': 'response'};
    var apiFunc = jest.spyOn(API, 'apiCall').mockImplementationOnce(() =&gt; {
      return Promise.resolve({
        json: () =&gt; Promise.resolve(fakeUserResponse)
      })
    })
    var {getByTestId, findByTestId} = render(&lt;App /&gt;)
    fireEvent.click(getByTestId("apiCall"))
    expect(await findByTestId("ptag")).toHaveTextContent('response');
  })
  </code></pre>
  
  <p>The error I'm getting is</p>
  
  <pre><code>expect(element).toHaveTextContent()
  
     Expected element to have text content:
       response
     Received:
  
       14 |   var {getByTestId, findByTestId} = render(&lt;App /&gt;)
       15 |   fireEvent.click(getByTestId("apiCall"))
     &gt; 16 |   expect(await findByTestId("ptag")).toHaveTextContent('response');
          |                                      ^
       17 | })
       18 | 
       19 | // it('api call returns a string', async () =&gt; {
  
  </code></pre>
  
  <p>Reusable unit test (hopefully):</p>
  
  <pre><code>    it('api call returns a string', async () =&gt; {
        const test1 = {'data': 'response'};
         const test2 = {'data': 'wrong'}
  
        var apiFunc = (response) =&gt; jest.spyOn(API, 'apiCall').mockImplementation(() =&gt; {
          console.log("the response " + JSON.stringify(response))
          return Promise.resolve(response)
          })
  
        var {getByTestId, findByTestId} = render(&lt;App /&gt;)
  
        let a = await apiFunc(test1);
        fireEvent.click(getByTestId("apiCall"))
        expect(await findByTestId("ptag")).toHaveTextContent('response');
        let b = await apiFunc(test2);
        fireEvent.click(getByTestId("apiCall"))
        expect(await findByTestId("ptag")).toHaveTextContent('wrong');
  
      })
  
  </code></pre>