TWIL
React
Emily discussing React test debugging on her laptop, with graphics illustrating API call mocks and error messages.

Embark on a journey through the intricacies of React testing with Emily's latest TWIL installment, where she tackles the elusive Debugging Network Errors in Tests. Discover how to effectively mock API calls and decipher those vexing network errors that often surface during automated test runs. Her insights offer a way to transform temporal bugs from intermittent CI failures into reproducible local issues, ensuring smoother development and more reliable software.

Debugging Network Errors in Tests

A key piece to testing in React is mocking out our API calls.

As part of our testing philosophy, we mock at the lowest level possible (e.g. mock an API response that loads data into the store and/or component instead of mocking a piece of data/props/state or the store itself).

Occasionally, we miss one of these mocks and it results in an error that looks something like this:

FAIL  app/containers/ClientPage/tests/ClientPage.test.js

  - ClientPage › renders and matches snapshot
 
    Network Error
      at createError (node_modules/axios/lib/core/createError.js:16:15)
      at XMLHttpRequest.handleError (node_modules/axios/lib/adapters/xhr.js:69:14)
      at XMLHttpRequest.<anonymous> (node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at XMLHttpRequestEventTargetImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at fireAnEvent (node_modules/jsdom/lib/jsdom/living/helpers/events.js:17:36)
      at requestErrorSteps (node_modules/jsdom/lib/jsdom/living/xhr-utils.js:121:3)
      at Object.dispatchError (node_modules/jsdom/lib/jsdom/living/xhr-utils.js:51:3)
      at Request.<anonymous> (node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:675:20)
      at Request.onRequestError (node_modules/request/request.js:877:8)

The trick here is that the Network Error is likely NOT coming from the ClientPage test. The network call was probably made in a previous test and just happened to time out while this test was running.

This type of bug also seems to pop up more frequently on CI or while updating snapshots locally (probably because these run a bit slower than when running the plain tests locally and the requests don't even have a chance to time out). This also means that this is a temporal bug - sometimes CI will pass and sometimes it won't!

Replicating the Error Locally

The easiest way that I've found to replicate this locally is to bump the timeout in the Axios instance down to something that will cause all API calls to time out almost immediately.

Your project probably has something like this, bump down that timeout:

// Default config options
const DEFAULT_OPTIONS = {
  baseURL: process.env.API_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 0.1, // Usually set to 15000, in ms
}

// Create instance
const instance = axios.create(DEFAULT_OPTIONS)

Bam! Now you can tell which test the error is actually coming from and see ALL errors deterministically:

Summary of all failing tests
 FAIL  app/containers/AgentPage/tests/AgentPage.test.js
AgentPage › does not log errors in console

    timeout of 0.1ms exceeded

      at createError (node_modules/axios/lib/core/createError.js:16:15)
      at XMLHttpRequest.handleTimeout (node_modules/axios/lib/adapters/xhr.js:77:14)
      at XMLHttpRequest.<anonymous> (node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at XMLHttpRequestEventTargetImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at fireAnEvent (node_modules/jsdom/lib/jsdom/living/helpers/events.js:17:36)
      at Timeout.properties.timeoutFn [as _onTimeout] (node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:729:15)

 FAIL  app/containers/ClientPage/tests/ClientPage.test.js
ClientPage › does not log errors in console

    timeout of 0.1ms exceeded

      at createError (node_modules/axios/lib/core/createError.js:16:15)
      at XMLHttpRequest.handleTimeout (node_modules/axios/lib/adapters/xhr.js:77:14)
      at XMLHttpRequest.<anonymous> (node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at XMLHttpRequestEventTargetImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at fireAnEvent (node_modules/jsdom/lib/jsdom/living/helpers/events.js:17:36)
      at Timeout.properties.timeoutFn [as _onTimeout] (node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:729:15)

 FAIL  app/components/ProfileSidebar/ProfileSidebar.test.js
ProfileSidebar › does not log errors in console

    timeout of 0.1ms exceeded

      at createError (node_modules/axios/lib/core/createError.js:16:15)
      at XMLHttpRequest.handleTimeout (node_modules/axios/lib/adapters/xhr.js:77:14)
      at XMLHttpRequest.<anonymous> (node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js:33:32)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at XMLHttpRequestEventTargetImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at fireAnEvent (node_modules/jsdom/lib/jsdom/living/helpers/events.js:17:36)
      at Timeout.properties.timeoutFn [as _onTimeout] (node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:729:15)

Now go mock that service!

  • React
Emily Morehouse's profile picture
Emily Morehouse

Cofounder, Director of Engineering

Related Posts

Marisa's presentation of a React application demonstrating Routing with PopoutForms, with highlights on code showcasing component management and URL dynamics.
May 19, 2020 • Frank Valcarcel

TWIL 2020-05-15

This week, Marisa unveils the secrets of Routing with PopoutForms in React, offering a practical guide to managing component visibility and URL parameters for improved UI.

Cuttlesoft's leadership team in a software design and planning meeting at our Denver headquarters
December 21, 2019 • Frank Valcarcel

Cuttlesoft Ranked a 5-Star Agency!

Cuttlesoft achieves levels of flexibility and efficiency that set us apart from the competition. Which is why our 5-star ranking on Clutch, a premier B2B buying guide, shows we can deliver.