What I Learned at Work this Week: Writing a Mocha Test

Photo by Karolina Grabowska from Pexels

Hopefully some readers can relate to what happened to me at work this week. I had picked up a ticket for a client that was seeing duplicate events reported on their site by my company’s software. I realized that this was happening because we were unwittingly attaching a new eventListener to the DOM when we reloaded our tag, but we weren’t removing the original eventListener. Because their site was a SPA (single page application), they were ending up with duplicate eventListeners, meaning a single event was being recorded multiple times.

I made a small change to our tag, tested it on the client’s site, and deployed it. Everyone was happy, but here’s the part that might sound familiar: fixing this issue for one client broke something different for a few other clients. A conversation ensued:

Me: I’m sorry that I caused this issue. I should have realized that this might happen for certain clients.

Coworker: Don’t beat yourself up too much. We really should have a test written for this behavior anyway.

Me: I guess you’re right. Thanks for saying that.

Coworker: Maybe you want to write that test

I’m still very new to the field, but I’m experienced enough to now have the instinct that says sure, I’ll give it a shot! I trust that my teammates wouldn’t suggest something to me unless they thought I could do it. So I started taking a look at Mocha this week.

Mocha is a JavaScript testing framework. It provides us with logic and a series of keywords that allow us to make assertions about how our JavaScript logic works. If those assertions are not true, Mocha will provide helpful messages about our tests failing. In this post, I’ll go over some of the keywords I saw in my company’s test files and attempt to build a simple Mocha file of my own. Feel free to code along!

To start, I’ll create a JS file and use the class keyword to build an object with attributes including functions that we’ll test.

This algo may or may not work

A few notes about this code: I chose a class because it’s easier to test the different aspects of a code when I can invoke them individually. The string attribute, for instance, would not be accessible to a simple test if stringManipulator were a classic function that saved this value as a variable. Also note that I am exporting my class — we’ll need to do this for anything we want to test, since we’ll be accessing it in another file.

And of course I hope you’ll Forgive me for the alorithm — I’ve been hearing a lot about Advent of Code recently and it’s made me feel bad that I haven’t practiced algos since starting my new job. Not bad enough to actually join Advent of Code, but bad enough to make this blog post a little more complicated than it needs to be. In total honesty — I did not run this code in my console or test it manually in any way. Odds are there’s a mistake in there somewhere, and to learn more about it, we’re going to write a Mocha test.

If you’re trying this at home, you’ll want to make sure you have Node installed, which will allow you to install Mocha. Check those hyperlinks if you’re having any trouble. To start, added Mocha to my test directory and created test file:

npm install mocha
mkdir test
cd test
touch stringManipulator.test.js

And here’s my result. (I’m using IntelliJ as my IDE and my version provided .idea, External Libraries, and Scratches and Consoles when I started a new project. Feel free to ignore those, at this point, I’ve manually created stringManipulator.js and stringManipulator.test.js. Running npm install mocha created and populated node_modules and also created package-lock.json.

To start, I’d like to write a test that I know will pass if it’s run. That way, I can make sure that I’ve set up Mocha correctly and that I know how to run a test:

The first thing I want to point out is that my IDE is showing some concern about describe and it, saying that they are unresolved functions. Apparently, this is IntelliJ checking whether my functions are actually defined in JavaScript. These keywords are native to Mocha and should therefore not be causing any issues if my installation ran correctly. I’m including this to reassure any readers that I was able to run my test successfully despite this warning.

Also note that I haven’t imported my StringManipulator class yet, since we’re not using it at the moment. If we’ve already tried to import it, we may run into an error when running the test. We’ll work on addressing that error later in this post.

To run the test, I followed the command from Mocha’s Getting Started documentation:

// From the same directory where I ran npm install mocha and mkdir test./node_modules/mocha/bin/mocha

We saw that running npm install mocha generated a node_modules directory with a ton of other directories inside. One of these is the mocha directory, which contains code and scripts that we can use to write and run Mocha! Running the command above actually runs the files in our test directory and gives us the results:

It worked! Okay so we weren’t actually testing the code we wrote, but at least we know the installation worked and we can run a Mocha test. Let’s look at our test one more time so that we can see what’s happening:

The first keyword is describe, which is useful for grouping tests. It simply puts a series of tests under a single category. In this case, that’s ‘string manipulator tests.’ Next we find it, which denotes a test. In both cases, our keywords take two arguments: a string and a function. The string is a description and the function is the logic we’d like to execute for our test. Let’s edit that logic inside of our it statement so that it actually tests something. We can add our import, create an instance of our class, and check the string property:

Go ahead and code this out and run the test to see what happens. If you get an Unexpected identifier error on your import like I did, this Stack Overflow post should help address that. Here’s the error for reference:

The short explanation here is that the import statement is using ES6 syntax, but Mocha runs on Node, which does not always play nicely with ES6. It needs help from Babel to decode the import, so the solution is to add Babel to the project and add a flag to our command.

Once we’re past that, we might actually see the results of our test! Mine failed:

We added a few keywords here: expect, to, and eql. I chose them because that’s what my test file used in the workplace, but these keywords aren’t built into Mocha. Instead, we’ve got a few different assertion libraries that we can choose from:

I learned that the keywords I’m looking to use are part of the Chai assertion library, so I added that to my project:

npm install chai

I also had to import the keyword:

And when I ran the test again…

Huzzah! It’s passing again! So we can see that our class constructor is set up properly. When the class is invoked with an argument of a string, that string is associated to the instance as a property. Next we can write a test to see if the reverseString method works:

As I feared, my algo didn’t work. I’m relieved, however, to see that the test suite is providing a helpful message. We can see that this.string is still ‘hello’ after running my method. And in looking back at said method, I can see why — I was writing in such a rush that I forgot to re-assign the property! Here’s the new version:

And here’s what happens after we run the test:

We made it.

Our test command (./node_modules/mocha/bin/mocha — require @babel/register) is pretty bulky and hard to remember. If you were reading Mocha’s Getting Started guide, you probably noticed that they include a test to simplify this by editing package.json. I’ll admit that I found this to be an extra challenge despite it seemingly only requiring one more line of code. The command provided in the documentation doesn’t include the babel flag and may resurface the Unexpected identifier error. Simply adding the require @babel/register flag didn’t work right off the bat for me, but I was able to successfully build the shortcut by following this DEV.to post. My takeaway from that process is to remember that package.json is simply a file built by scripts and that I shouldn’t be afraid to edit or even reset it. Sometimes it’s giant, but for the most part it’s legible and, if we know all of our dependencies like in this simple example, it’s safe to simply delete it completely and start over with a new npm command. Just make sure you know everything you need, or you might delete something and forget to re-add it.

The second thing you might notice is that the Mocha documentation warns against using arrow notation for functions like I did in my examples. I used arrow notation because that’s how my workplace codebase looks, but remember that arrow functions lexically bind this. This is often helpful for me because it’s rarely the intent of my code to look outward for a this context, but Mocha actually has a set of built-in methods that can be referenced using this. Some examples of Mocha methods include this.timeout(), this.skip(), and this.retries(). If we want to invoke those, we’ll have to stay away from arrow functions.

One other thing that stood out to me when looking at my codebase from work was the done function. I would see code that looked like this:

describe(‘a new test suite, () => {
beforeEach(done => {
setTimeout(done, 400);
it(‘checks something’, () => {…})

I learned that the done function is used in asynchronous testing to indicate when a test may be completed. The test will not resolve and provide a response until done is invoked, so in the case of my company’s codebase, I would expect that the setTimeout is designed to give postMessage the appropriate amount of time to resolve. If we attempt to run the subsequent tests before the message is resolved, many of them will fail because of undefined values.

I used a quick and stripped-down example for this post, but it still taught me some important lessons. Besides everything I learned about Mocha, I was also reminded that there are a lot of available resources when we’re trying to learn something new. I had never written a Mocha test before and I ran into quite a few hurdles like forgetting to import and neglecting to install or invoke critical libraries. As always, forums like Stack Overflow and official documentation helped address exactly what I was dealing with. Be patient and read thoroughly.

Solutions Engineer