jest: asserting on classnames of css modules with identity-obj-proxy

2021-05-05

 | 

~3 min read

 | 

457 words

While testing specific CSS styles within a unit test is rare, it’s not uncommon to want to know if the right class was applied to a component.

In Jest: Handling Static Assets with Mocks, I walked through how to mock out CSS modules so that they wouldn’t break tests because Jest didn’t know how to handle them.

Unfortunately, the mock removes all detail from the component or requires a separate mock for each test (which is hardly ideal). An NPM package, identity-obj-proxy provides a pretty elegant solution to this problem. Let’s see how!

What Is The Problem?

Before seeing how identity-obj-proxy solves the problem, let’s make sure we understand. We’ll assume that we’ve already set up a mock CSS file and we have the following component:

MyComponent.js
import React from "react"
import PropTypes from "prop-types"
import styles from "./my-component.module.css"

function MyComponent(props) {
  return <div className={styles.myClass}>{props.children}</div>
}

A CSS module which defines the class

MyComponent.module.css
.myClass = {
  /*...*/
}

And finally, the test:

MyComponent.test.js
import React from "react"
import { render } from "@testing-library/react"
import MyComponent from "./MyComponent"

test("it renders", () => {
  const { debug } = render(<MyComponent />)
  debug()
})

When I run the test, I’ll see the following logged by the debug call:

PASS  src/MyComponent.test.js
  ● Console

    console.log
      <body>
        <div>
          <div
            data-testid="total"
            style="transform: scale(1,1);"
          />
        </div>
      </body>

      at debug (node_modules/@testing-library/react/dist/pure.js:107:13)

Notice that there’s no className! It’s hard to make an assertion that the right class is applied if it gets stripped out completely.

Let’s fix that.

Seeing Classes With Identity-Obj-Proxy

First, we need to install the library:

% yarn add --dev identity-obj-proxy

Then, we’ll need to add it to our jest.config.js:

jest.config.js
module.exports = {
  moduleNameMapper: {
+     '\\.module\\.css$': 'identity-obj-proxy',
    '\\.(css|less)$': require.resolve('./test/style-mock.js'),
  },
}

Note: Order matters. If the .module.css mapper came after the .css variant, it would not be used.

Once in place, if we re-run our test, we’ll see the class is passed through with the name of the class as defined in the module:

PASS  src/MyComponent.test.js
  ● Console

    console.log
      <body>
        <div>
          <div
            class="myClass"
            data-testid="total"
            style="transform: scale(1,1);"
          />
        </div>
      </body>

      at debug (node_modules/@testing-library/react/dist/pure.js:107:13)

Wrap Up

The purpose of this post was to walk through how we can use identity-obj-proxy to get access to details that might otherwise be obscured through the use of mocks so that we can make assertions on them in our tests. This post walked through that exercise using CSS modules as the subject.

It’s also worth a reminder that order matters when it comes to moduleNameMapper as matches are done on a first seen basis - so, place the most specific matches at the top of the mapper and least specific at the bottom.


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!