Jest is a fantastic testing library, but maybe you've been putting off the switch because migrating all of your existing tests from another library seems like a daunting task. With jest-codemods, you can automate most, if not all, of that migration! The jest-codemods CLI is a wrapper around jscodeshift that is specifically focused on helping you migrate from several popular test runner and assertion library combinations to Jest. In this lesson, we'll walk through converting a small sampling of tests created with Mocha, Sinon and Chai over to Jest. We'll even take a look at a couple edge cases where the conversion requires some manual intervention so you can be prepared for those in your own project.
Instructor: [00:00] In this file, we have a handful of utility functions. There are a few to perform math, a function to type check a value, a utility to pull a property off an object, and a function called once that limits a passed-in function to a single execution.
[00:13] In index.spec.js, we have unit tests for all of these utility functions. We're using Mocha as our test runner, and we're using the expect function that's made available through Chai. We also have Sinon being imported here, and we're using that to stub out a function in one of our tests.
[00:27] Now, if I open up the terminal and I type npm test, we can run these tests. We'll see that we have 10 passing tests. Now, I'd like to switch my test runner from Mocha to Jest. In order to do that, I'm going to start off by creating a new branch in Git, just in case things don't go as planned.
[00:45] I'll git checkout -b, and I'll call the switch to Jest. Now, we have our new branch. We can make changes safely, and we can always get back to that working state if we need to. The first thing I'll do in this new branch is I'm going to add Jest as a dependency to this project.
[01:02] I'll use npm i -d to save it as a dev dependency, Jest. Now that Jest is installed, I'm going to open up the package.json, and I'm going to update my test script to use Jest. For now, I'm just going to keep these old scripts around and update their names, so that we have them as a reference.
[01:25] Then I'll create a new test script. That's simply going to call Jest. Then can we create a new test:watch script. That is going to call Jest with the watch flag. We'll save that. With that done, let's switch back to the terminal, run npm test again, and see that how goes.
[01:48] We'll see that our tests are failing because all of our test code was written for Mocha and Chai, not for Jest. We need to go through, and we need to change our test code to be compatible with Jest. Doing this manually would be time-consuming and error prone, so we're going to do this using a codemod.
[02:05] The first step is to install the Jest codemod CLI. I'm going to install this globally, because it's going to give me a command line utility. npm i -g jest-codemods. Now that we have the Jest codemod CLI available, we can use it to transform our code.
[02:25] We use this tool through the CLI. We'll type jest-codemods, and then we need to tell it which files we want to transform. I'm going to say everything under the source directory that ends in spec.js. I'll press enter, and it's going to run the tool.
[02:42] It's going to give us a warning here. It's going to say that we need to stash or commit our changes before we start. That's a good idea. Let's just see what our status is. We've modified our package.json and the package lock, because we installed Jest as a dependency.
[02:58] We can commit that. We'll just git add all and git commit -m. We'll see that we added Jest. Then we can git status again, and we're good. Now, we should be able to just up arrow back to our jest-codemods command and try running it again.
[03:19] We'll press enter, and it's going to prompt us with some choices. It wants to know what test library we're migrating away from. In this case, we're using Mocha, so we're going to migrate from Mocha. It gives us a choice between the globals provided by Jest, which is the recommended setting, or using explicit require calls to pull those globals in.
[03:38] We're going to use the globals from Jest. Then we need to pick how we're going to handle our assertions. Currently, we're using the should/expect BDD syntax from Chai, so we'll select that option. We'll see that it's processed our files, and our results are zero errors, zero unmodified, zero skipped, and one OK.
[03:59] Let's use a git diff to see what's changed. I'm going to clear this out, and I'm going to run git diff. We'll see that the first line in the file is our import for expect from Chai, and that's been deleted. If we drop down into the first describe block, we'll see that our context and it lines have been changed to describe and test.
[04:18] We'll see that that change has been made throughout. Every time there's a context or an it, it's been changed to a describe or a test. We'll also notice in the first test that our expect result to.equal has been changed to expect result toBe. Now, it's using the Jest assertions, rather than the expect assertions from Chai.
[04:38] If we go to the next set of changes, we'll see more changes where our assertions have been updated to use the Jest assertions, and our describe and test blocks have replaced our context and it blocks. Here, we can see additional changes to expects with different assertions, where they've been converted over.
[04:57] We've automated the update to our spec file. Let's make sure our tests still pass. I'm going to run npm test. Jest runs our test, and we have nine passing and one failing. Only one test didn't get converted over 100 percent.
[05:12] We'll see that we get a type error, "Cannot read property eq of undefined." We see that it still has our old expect syntax from Chai. The problem here is that eq is an alias for equal, and that didn't make it through the conversion.
[05:25] That's easy enough to fix. We can just go into index.spec.js, and we can find that test. We can update this to a toBe, and we can save it. Then let's run our tests again. I'll do npm test. Now, everything's passing.
[05:46] Now, we just have to deal with that warning we saw when we did our conversion about Sinon possibly being incompatible with Jest. It seems like everything's working now, but there's no reason to have a separate mocking library installed when Jest will do that just fine.
[06:00] Let's go into our tests, and we'll find that spec. It should be the last one. We'll see here we're using Sinon.stub to create our stub function, and then making an assertion using Sinon's assertion library. We can update this, and we can just use the global jest.function.
[06:18] That'll give us back our spy function. Then we can come down here, and we can use jest expect. We can say we expect function to have been called times one. Then back in our terminal, we can run our tests again, just to make sure that that works.
[06:39] All of our tests are passing. We no longer need Sinon. Back in our file, we'll jump up to the top. We can delete that import. Then in our package.json, we can find Mocha, Chai, and Sinon in our dependencies and get rid of them.
[07:00] Now, we've successfully converted from Mocha, Sinon, and Chai over to using just Jest, and we've removed those unnecessary dependencies. There's one more cleanup item I'd like to do. Back in index.spec.js, we have this test for this prop utility that's expecting the result to not be undefined.
[07:22] Jest gives us a better way to do this. We can just use this toBeDefined function that make that assertion a little more explicit. We can save this, jump back into the terminal, run npm test one more time, just to make sure everything's still passing, and we're good. Now, the only thing left to do is commit and submit a PR.