reactjsjestjsreact-router-domsnapshotreact-test-renderer

Jest snapshot test logs errors messages BUT app renders and seems to function correctly


I've recently started using the Jest snapshot library in order to get familiar with front end testing. An issue I've come across are its error messages. I get error messages despite the app seemingly functioning correctly.

For example:

enter image description here

Here's my snapshot test for this

/**
 * @jest-environment jsdom
 */

import renderer from 'react-test-renderer';

import { App, Login, Footer, NavBar } from '../components/testExports.js';

it('renders <NavBar> correctly', () => {
  const tree = renderer
    .create(<NavBar/>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

Here are the logged error messages:

enter image description here

enter image description here

Reading the error messages took me to the node_module react-router-dom/index.tsx:360:7 and I found this function which to my understanding looks like is responsible for creating links in the RRD component enter image description here

What doesn't make sense to me is that the useHref() function IS in a <Router> component, not directly but if you look at the DOM tree in the react devtools screenshot NavBar is a child component of other React components that are direct children of the <Router>

Here's a simplified snippet of my component top-bottom to demonstrate this further

/*////index.js////*/

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App.js';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
  <App/>
</BrowserRouter>
);


/*////App.js////*/

import React, { useState, useEffect } from 'react';
import Router from './Router.js';
const axios = require('axios');
const App = () => {

  return (
    <div id='app'>
      <Router userAuth={userAuth} getUserAuth={getUserAuth} userLogin={userLogin} authorized={authorized} userRegister={userRegister} />
    </div>
  );
}

export default App;

/*////Router.js////*/

import React from 'react';
import { Routes, Route, Link } from 'react-router-dom';
import Home from './Home.js';
import Login from './pages/Login.js'

const Router = ({ userAuth, getUserAuth, userLogin, authorized, userRegister }) => {
  return (
    <Routes>
      <Route path='/' element={ <Home userAuth={userAuth} getUserAuth={getUserAuth} authorized={authorized} /> }/>
      <Route path='/login' element={ <Login userAuth={userAuth} getUserAuth={getUserAuth} userLogin={userLogin}  authorized={authorized} userRegister={userRegister} /> }/>
    </Routes>
  );
};

export default Router;

/*////Home.js////*/

// Global Modules
import React, { useState, useEffect } from 'react';

// Containers
import { Globe, Info, MakeYourOwn, CustomPlanet, CustomInfo, CustomGlobe, NavBar, Footer } from './Imports.js';
const axios = require('axios');

const Home = ({ userAuth, getUserAuth, authorized }) => {
  
  // Main function that renders the planet onto the page
  const newPlanet = () => {
    if (state.build) { // if we're done building our custom planet
      return (
        <div id='home'>
          <NavBar userAuth={userAuth} getUserAuth={getUserAuth} authorized={authorized} />
        <div id='home-body'>
          <Info
          func={alertFunc}
          data={state.data}
          makeyourown={makeyourownClick}/>
          <Globe planetName={state.planetName} />
        </div>
        <Footer/>
      </div>
      );
    }
  }

  return (
    <div id='container'>
      {newPlanet()}
    </div>
  );

}

export default Home;

Bottom line, is there something functionally wrong with my code (there doesn't appear to be, the links work and they render all the UI component successfully)? Or is the message more of a best practices thing? I've read in an article that snapshot testing has nothing to do with testing whether the behavior of a component is correct.

ref: https://www.sitepen.com/blog/snapshot-testing-benefits-and-drawbacks

Is there something about snapshot testing that I'm not understanding? Any suggestions on how to move forward?

Here's also my package.json and my babelrc in case that might help, there don't seem to be any dependency issues

{
  "name": "newapp",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "build": "npx webpack",
    "test": "jest",
    "server": "nodemon server/server.js",
    "client": "npx webpack --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.18.10",
    "@babel/preset-env": "^7.18.10",
    "@babel/preset-react": "^7.18.6",
    "@react-three/drei": "^9.20.0",
    "@react-three/fiber": "^8.3.1",
    "axios": "^0.27.2",
    "babel": "^6.23.0",
    "babel-jest": "^29.3.1",
    "babel-loader": "^8.2.5",
    "dotenv": "^16.0.1",
    "express": "^4.18.1",
    "jest": "^29.3.1",
    "nodemon": "^2.0.19",
    "path": "^0.12.7",
    "pg": "^8.7.3",
    "postgres": "^3.2.4",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.4.0",
    "react-router-dom": "^6.4.3",
    "react-test-renderer": "^18.2.0",
    "styled-components": "^5.3.5",
    "three": "^0.143.0",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  },
  "devDependencies": {
    "file-loader": "^6.2.0",
    "jest-environment-jsdom": "^29.3.1"
  }
}

{
  "presets": [
  "@babel/preset-env",
  [ "@babel/preset-react", {runtime: 'automatic'} ]
  ]
}

Solution

  • It should be fairly obvious that in the unit test the NavBar component is not rendered within a routing context.

    it('renders <NavBar> correctly', () => {
      const tree = renderer
        .create(<NavBar />) // <-- just the NavBar, no router
        .toJSON();
      expect(tree).toMatchSnapshot();
    });
    

    Remember that in unit tests you are testing single units of your code, not the entire application. In the above test you are testing the NavBar component in isolation from all other code. This doesn't mean that they still work outside any React contexts they are accessing, i.e. redux stores, routing/navigation contexts, etc.

    Wrap the NavBar component in a router such that it has a routing context available to it. The MemoryRouter is often used for unit testing.

    Example:

    import renderer from 'react-test-renderer';
    import { MemoryRouter } from 'react-router-dom';
    import { App, Login, Footer, NavBar } from '../components/testExports.js';
    
    it('renders <NavBar> correctly', () => {
      const tree = renderer
        .create(
          <MemoryRouter>
            <NavBar />
          </MemoryRouter>
        )
        .toJSON();
      expect(tree).toMatchSnapshot();
    });