Skip to main content

Lerna Monorepo Mocha Setup (JavaScript)

In this article, I show you how to set up a lerna JavaScript monorepo for testing with mocha (MochaJS).

These instructions were written and tested on a Mac.

Step 1. Install lerna and mocha globally

Install lerna and mocha globally using npm.

If you don’t have npm installed, click here.

  • Open up a terminal window
  • Run the following (the use of sudo is optional and will require a password):
sudo npm install --location=global npm@latest lerna mocha

Because the packages are installed globally, you won’t need to do this again for new projects.

Step 2. Create a monorepo project

  • Change to a parent folder for all of your projects
  • Create a new folder for a new lerna monorepo project:
mkdir lerna-javascript-example
cd lerna-javascript-example

Step 3. Decide how you want to handle versioning

There are two ways to handle package versioning in lerna:

  • fixed – (the default) every package is published with the same version
  • for fixed versioning, you would initialize with lerna init
  • independent – packages are published independently with their own version
  • for independent versioning you would initialize with:
    lerna init --independent

The focus of this article is on testing and not publishing. So for this example, you can just go with the default (fixed).

  • Initialize lerna:
lerna init
npm install
  • Verify that the following files and folders are listed:
lerna.json
node_modules
package-lock.json
package.json
packages

Step 4. Edit .gitignore file

  • A new file called .gitignore should have been created in the root of your project
  • Open .gitignore in a text editor
  • Paste the following into the file and save it:
node_modules/
lerna-debug.log
npm-debug.log

Step 5. Create a package

  • Create a new package by running this command from the root of your project:
lerna create demo-tools
  • Fill in the details (replace @mitchallen with your npm scope, if any):
package name: (demo-tools) @mitchallen/demo-tools
version: (0.0.0)
description: Demo tools module
keywords: demo
homepage:
license: (ISC)
entry point: (lib/demo-tools.js)
git repository:

You should see a message about a new packages folder and package.json file.

The path for the JSON file should be something like this:

  • lerna-javascript-example/packages/demo-tools/package.json

The contents should be echoed to the console and look something like this:

{
"name": "@mitchallen/demo-tools",
"version": "0.0.0",
"description": "Demo tools module",
"keywords": [
"demo"
],
"author": "Mitch Allen <...>",
"homepage": "",
"license": "ISC",
"main": "lib/demo-tools.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
  • Confirm when prompted

Run npm install

From the root of your project, run npm install:

npm install

Make the package public

This article doesn’t cover publishing. But if you are planning to publish the package at some point, be sure to add a publishConfig section to package.json in the package folder.

For example:

"publishConfig": {
"access": "public"
},

You will need to paste that into every new package that you want to give the public access to.

Step 6. Create the library

  • Replace the code in packages/demo-tools/lib/demo-tools.js with the following and save it:

It’s a simple module that has two functions:

  • add – returns the addition of two arguments
  • subtract – returns the subtraction of one argument from another

Step 7. Add a test script to the package

  • Add this to the scripts section of the demo-tools package.json file (if there's already a test script, replace it with this one):

  • Remember to add this script whenever you add a new package

  • Edit packages/demo-tools/__tests__/demo-tools.test.js

  • Replace all code in the file with the following and save it:

"scripts": { 
"test": "mocha __tests__/*.test.js"
}
  • Remember to add this script whenever you add a new package

Step 8. Write the demo-tools test cases

  • Edit packages/demo-tools/__tests__/demo-tools.test.js
  • Replace all code in the file with the following and save it:
'use strict';

var assert = require('assert');

const demoTools = require('..');

describe('demo-tools', function () {
context('smoke test', function () {
it('add should add two numbers together', function (done) {
assert.strictEqual(demoTools.add(100,200),300);
done();
});
it('subtract should subtract one number from another', function (done) {
assert.strictEqual(demoTools.subtract(100,200),-100);
done();
});
});
});

Step 9. Run the tests

Run the tests with the following command:

lerna run test

You should see the mocha test results for your package.

When you have more than one package, the command will run all test scripts in all packages.

Step 10. Add a second package

Add a second package called demo-main, just like you did for the first package:

lerna create demo-main
  • Replace the code in packages/demo-main/lib/demo-main.js with the following (replace YOUR-SCOPE in the require statement with your npm scope (in my case that would be @mitchallen)):
"use strict";

var tools = require('@YOUR-SCOPE/demo-tools');

module.exports = demoMain;

function demoMain(a,b,c) {
// return a + b - c
return tools.subtract(tools.add(a,b),c);
}

Run npm install

From the root of your project, run npm install:

npm install

The code uses functions from the demo-tools library to calculate and return a value.

This is to prove that the demo-main package can require (import) the tools package using the lerna framework.

But first, you need to add a dependency.

Step 11. Add a dependency

For the code to work, you have to add the tools package as a dependency for the main package.  

But beginning with Lerna 7.x the lerna add command will be deprecated. This is because new versions of npm have added monorepo support (workspaces), making the lerna command redundant. I've included a link regarding legacy package management in the references at the end of this article.

So first I will show you the legacy way to add a package.  Then I will show you the new way of using npm workspaces.

Legacy command: lerna add

In case you find yourself maintaining a project using an older version (Lerna 6.x or less), here is how you would add a dependency the legacy way:

  • To create a dependency, you would run a command like this:
lerna add @YOUR-SCOPE/LIBRARY --scope=@YOUR-SCOPE/PACKAGE
  • Substitute YOUR-SCOPE with your npm scope
  • Substitute LIBRARY with the name of the library that the package needs
  • Substitute PACKAGE with the name of the package that needs to use the library

Here is an example:

  • Run this command (substitute mitchallen with your npm scope):
lerna add @mitchallen/demo-tools --scope=@mitchallen/demo-main
  • Verify that the dependency was added to the package.json for demo-main:
  "dependencies": {
"@mitchallen/demo-tools": "^0.0.0"
}
  • Your scope in the dependency should be different from mine

Adding a dependency using npm workspaces

Before using the npm workspace commands to add a package, you need to confirm that your lerna project is using npm workspaces (older projects may not).

To confirm, check the following in the root of your project:

  • lerna.json contains this line:
"useWorkspaces": true,
  • package.json contains:
"workspaces": [ 
"packages/*"
],

Create a workspace dependency

  • To create a dependency using npm workspaces, you would run a command like this:
  • Substitute YOUR-SCOPE with your npm scope
  • Substitute LIBRARY with the name of the library that the package needs
  • Substitute PACKAGE with the name of the package that needs to use the library
npm install @YOUR_SCOPE/LIBRARY -w @YOUR-SCOPE/PACKAGE

Here is an example:

  • Run this command (substitute mitchallen with your npm scope):
  • Your scope in the dependency should be different from mine
npm install @mitchallen/demo-tools -w @mitchallen/demo-main
  • Verify that the dependency was added to the package.json for demo-main (don't panic if it doesn't appear yet, see below):
  "dependencies": {
"@mitchallen/demo-tools": "^0.0.0"
}
  • If it doesn't appear, run npm install once or twice - sometimes it doesn't work the first time

Step 12. Write the demo-main test cases

  • Edit packages/demo-main/__tests__/demo-main.test.js
  • Replace all code in the file with the following and save the file:
"use strict";

var assert = require('assert');

const demoMain = require('..');

describe('demo-main', function () {
context('smoke test', function () {
it('should add first two numbers and subtract the third', function (done) {
const a = 100, b = 200, c = 50;
const expected = a + b - c;
assert.strictEqual(demoMain( a, b, c ), expected );
done();
});
});
});
  • Add a test script to the child packages package.json file and save it:
"scripts": { 
"test": "mocha __tests__/*.test.js"
}

Step 13. Run all the tests

Run all the tests as you did before with:

lerna run test

You should see results for both packages.

Run tests for just one package

To run the tests for just one package, use the scope command (substitute my scope for your scope):

lerna run test --scope @mitchallen/demo-tools

If you are in a packages folder, you can also run the tests like this:

npm test

Once everything passes, you have the option to publish to npm or a private repo. But that’s beyond the scope of this article.

Troubleshooting

No rights to install lerna globally

  • On a Mac or Linux, try putting sudo in front of the command

Get the Error: run tests from root

  • This actually might mean that you forgot to add the test script for a package

Conclusion

In this article you learned how to:

  • Setup a lerna JavaScript monorepo for testing using mocha (MochaJS)
  • Add multiple packages
  • Add mocha for testing
  • Create dependencies between packages without the need for linking or publishing

References

  • Lerna – A tool for managing JavaScript projects with multiple packages [1]
  • Lerna Legacy Package Management [2]
  • npm workspaces [3]
  • Things I wish I had known when I started JavaScript monorepo with Lerna [4]