Testing private JavaScript functions using Babel
At JetClosing we utilize decomposition and modular programming. As projects get larger, it’s important to clearly define the interface between objects by exporting the methods that make up that interface. Sometimes this can cause issues when trying to unit test that code, especially the private functions. The existing strategies for accomplishing this have their own issues:
- Exposing private members - This creates potential security issues and can effect development; such as when auto imports suggest the intended private members over other public members.
- Testing through public functions - This approach is a pain! In order to have targeted tests for the private functions, it requires a bunch of setup and teardown code just to ensure the private function is executed.
- Use existing 3rd party options - The current options are limited, and require multiple plugins or custom test imports; such as
denude
andbabel-plugin-test-export
.
None of these strategies are ideal or practical in large projects, so we decided to create our own solution as a single Babel plugin: babel-plugin-testable
. Like most modern javascript developers, we are using Babel to compile modern javascript into backwards compatible code. Therefore, we made a plugin that could export our ES6+ code for testing purposes.
Integrating the plugin
Install
Install the plugin using your favorite package manager: npm install -D babel-plugin-testable
or yarn add -D babel-plugin-testable
.
Enable
Add the plugin to your Babel configuration:
Using the Plugin
Now, decorate and import!
Decorate
Add testable comments to the code you want to test:
Import
Import your private members along with the public members in your tests, and add any test code you need:
That’s it!
Now you can import any private function, variable, or class for testing purposes. Simple and clean!
The Nitty Gritty
So how does it work? This is pretty simple too!
When compiling the code, the plugin keys off of the NODE_ENV
or BABEL_ENV
environment variables to determine if the code is being compiled for tests. If neither of these are 'test'
, then it compiles as it normally would. Otherwise, it looks for top level private members (variables, functions, and classes) that are annotated with the // @testable
comment, and adds an export
in front of them. This allows the private members to be imported in your test files like any other exported member.
Give it a shot!
Now that we’ve made testing private members easier for you, give it a shot! Let us know what you think in the comments.