Taking Away the Pain From Unit Testing in Google Apps Script

Dmitry Kostyuk
Apr 7 · 11 min read

Why It’s Crucial

Unit testing is an extremely important practice. It helps developers avoid bugs easily and lets you know when something is broken immediately. This makes refactoring easier. There is a great overview regarding the reasons why it’s a must in a recent article by Tyler Hawkins. Inexperienced developers tend to rush into coding and more often than not end up chasing bugs for hours or days before they get things to work again. If you are unfamiliar with unit testing, please feel free to pause right here, read up, and come back. I’ll wait, I promise! 😎

  • It must be lightweight.
  • It must be easy to maintain.

What’s Inside

Let’s see what’s inside the library. There is a small class called UnitTestingApp with just a few simple functions. After all, “lightweight and easy to maintain,” remember? There is also the MockData class that allows you to add and work with, well, mock data, which is especially important when running tests offline. There will be more on that later.

Enabling, Disabling, and Checking the Status of the Tests

The two methods, enable(), disable(), and the isEnabled property, are hopefully self-explanatory. The syntax is:

Choosing the Environment with runInGas(Boolean)

The runInGas(Boolean)function allows developers to choose the environment in which we want the tests that follow to run, as follows:

assert(condition, message)

assert() is the main method of the class. The first argument that it takes is the condition, and it checks whether the condition is truthy or falsy. The condition can either be a boolean value or a function that returns such a condition, with the function being the preferred approach for error-catching reasons. If the condition is truthy, it logs out a “PASSED” message. Otherwise, it logs out a “FAILED” message. If you pass in a function that throws an error, the method will catch the error and log out a “FAILED” message. For example:

catchErr(callback, expectedErrorMessage, message)

The goal of this method is to test whether your callback function (callback) catches the correct error. What you want to do is make sure that the callback actually throws an error. Then, the catchErr()method will check if it’s the correct one. Finally, it will log out the corresponding message. For example, let’s say you have a function that returns the square value of a number you pass as the argument, and you want to throw an error if the argument isn’t a number. Then, you can test the error this way:

is2dArray(Array)

This method runs a test to check whether the argument is a 2D array. This is useful for testing spreadsheet values before inserting them into a spreadsheet:

printHeader(headerStr)

This helps with readability by printing a header in the console like this:

clearConsole()

This is a straightforward method that clears the console log if you are in the local environment.

addNewTest(functionName, func)

Let’s say, for example, that we need a test that would check whether a number is even:

Thinking the Project Through

The app we will be building is a simple one. It will pull calendar data for the coming two weeks, save it in a spreadsheet, convert it into an HTML table, and send it by email.

  • whether it throws an error when one of the arguments isn’t a date

Setting Up the Local Environment

For local GAS development, VS Code is the best IDE, as it supports autocomplete. For autocomplete to work, make sure you have node.js and npm installed. Then, you will want to install the following package by running this command in your terminal:

npm install --save @types/google-apps-script
npm install @google/clasp -g
  1. I copy its ID.
  2. Then, I create a local folder for my project and navigate there in my terminal.
  3. If I’m not logged in, or if I’m logged in with a different account, I run clasp login.
  4. Then, I run clasp clone “<PROJECT_ID>”, which sets up the sync between my actual project and my local folder.
  5. Then, I run clasp push -w, which syncs my code to the server every time I save a local file.
  6. To stop pushing code online when I’m done, I type Cntl+C.

Setting Up the Tests Environment

It is a best practice to write your tests before you write the code. You will have your tests showing the FAILEDresults, and as you progress through your code, they will turn to PASSEDone by one. When they all pass, because you thought your tests through thoroughly, including the edge cases, you will know that your code is stable.

Writing the Tests

Above, we have already determined the tests that we need to write. Let’s now actually write them and put them in the offline tests block for now:

Creating the Events Class

Now we know that we want to be able to initiate an Eventsclass with new Events(startDate, endDate)and have a get()method retrieve the data as a 2D array. It is also a best practice to add a print()method to your classes so that you can log out its contents easily. We will need this method later on.

Pulling the Calendar Data

Now that we need to pull the calendar data, we need to execute the tests in online mode. Let’s continue writing our class.

test.is2dArray(events.get(), 'Calendar Data is a 2D array');

Using Mock Data for Local Testing

Our calendar data has the following format:

2D Array to HTML Conversion

We’re now back in offline mode.

Building the App

I find that knowing for certain that you have working classes before you begin building the app is of great comfort. In the testing file, you may now change `test.enable()` to `test.disable()` and create your app file. Depending on your needs, the main function of your app may look something like this:

Conclusion

We now have a very organized app with stable classes that we’ve thoroughly tested. And when you need to add more code to them, just switch the tests back on and make sure old tests keep working. This will ensure that your code will stay backwards-compatible and not break anything anywhere.

About the Author

Dmitry Kostyuk is a full-time GAS developer and Founder of Wurkspaces.dev.

Geek Culture

Proud to geek out.

Sign up for Geek Culture Hits

By Geek Culture

Subscribe to receive top 10 most read stories of Geek Culture — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Dmitry Kostyuk

Written by

Full-time GAS developer, founder at Wurkspaces.dev

Geek Culture

A new tech publication by Start it up (https://medium.com/swlh).

Dmitry Kostyuk

Written by

Full-time GAS developer, founder at Wurkspaces.dev

Geek Culture

A new tech publication by Start it up (https://medium.com/swlh).

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store