Two Ways to Run Tests on Different Versions of The Same Library F.E. React 17 + React 16

How can you run tests on version X and version Y of a library, with real-world examples of testing React 16 and React 17

With the release of React 17, many React library maintainers found themselves wondering:

I’ve certainly found myself in this situation with my library Why Did You Render.

I decided to find a way to test both of the versions since my library deals with patching React ( don’t try this at home :P ) and works a little differently for each of the versions.

The First Way: Yarn Aliases

This is the approach I decided to use in the end.

You can find the full example on version 6.0.3 of Why Did You Render.

What I did was to add the React 16 libraries as yarn-aliased dependencies:

Which results in yarn installing react, react-dom, and react-is of version 16 along with their version 17 packages in node_modules:

A screenshot from node modules where react-16 is installed along with react
A screenshot from node modules where react-16 is installed along with react
A screenshot from my node_modules folder where react version 16 is installed along with version 17

Now we only need to make sure that when a certain ENV variable is present, Jest uses the correct version of the library.

One of doing this is to use moduleNameMapper in jest.config.js:

And now both tests can be run by roughly the following npm script:

"test:react-17": "jest --config=jest.config.js",
"test:react-16": "USE_REACT_16=true jest --config=jest.config.js",
"test": "yarn test:react-17 && yarn test:react-16"

That’s it!

But sometimes we would like a little more control over the mocking —

Manual Mocking

We can also manually mock dependencies. It might be useful in more complicated cases where we would like to have more control over how the packages are being aliased.

I used this approach on version 6.0.2 of Why Did You Render.

The config setupFilesAfterEnv defines a list of files to run before each test file. We set it to run jestSetup.js:

where jestSetup.js is:

But notice that we also need to mock react-dom/test-utils (on line 10) and any other specific import. This is why I decided to go with the moduleNameMapper approach that I mentioned before.

This approach gives us a little more control, but the first way is faster and more clear.

The Other Way: Separate Folder

That’s actually the first way that truly worked for me. I didn’t like it because it felt too hacky and vulnerable. But I think we can learn a lot from understanding how it works. I can see this way actually being useful in certain scenarios and frameworks.

You can find it’s implementation on version 6.0.1 of Why Did You Render.

In this way, we rely on module resolution to use React 16 (or any other set of libraries). We run the tests from a folder with its own node_modules where React 16 is installed.

So first of all we open a new folder to serve as the starting point for the second set of tests. F.E test-react-16.

Then we create package.json in that folder, with all the libraries we want to test there, instead of the ones installed in the root package.json:

we make sure that whenever we install our packages, the packages of this new folder would be installed as well, using the prepare npm script, that runs whenever you run yarn.

Here how it looks:

"prepare": "cd test-react-16 && yarn",

And this is how running yarn looks like with this approach:

Then, we create a special jest.config.js that:

  • Explicitly sets the rootDir of jest to be the real root folder outside of the new test folder.
  • The folder’s node_modules takes precedence over the main node_modules using the moduleDirectories setting.

We also need to take care of other issues like to make sure that babel works correctly, by adding a babel.config.js file to the new folder that points to the one outside of it:

The final folder with its own node_modules looks like this:

The other folder with its own node_modules

now we just need to run the tests from the new folder:

"test:react-17": "jest --config=jest.config.js",
"test:react-16": "cd test-react-16 && jest --config=jest.config.js",
"test": "yarn test:react-17 && yarn test:react-16"

That’s it!

And again, I would stick to the first way.

Thank You!