And now, two months having gone by since it got published, I think it’s finally time to post about it.

mongodb-sandbox will launch a stand-alone MongoDB Topology for use within a Test Suite. It spins up a self-contained instance of mongod on a free local port and performs all the setup & teardown necessary to ensure an empty database at the start of each Test Case.

For example, using mocha,

const { expect } = require('chai');
const { MongoClient } = require('mongodb');
const { createSandbox } = require('mongodb-sandbox');


const sandbox = createSandbox();

before(function() {
  const lifecycle = sandbox.lifecycle(this);

  before(lifecycle.beforeAll);
  beforeEach(lifecycle.beforeEach);
  afterEach(lifecycle.afterEach);
  after(lifecycle.afterAll);
});


describe('the Sandbox', () => {
  it('is running', () => {
    expect(sandbox.isRunning).to.equal(true);
  });

  it('can be pinged', () => {
    const { url } = sandbox.config;

    return MongoClient.connect(url, { useNewUrlParser: true })
    .then((client) => {
      return client.db().admin().ping()
      .then((response) => {
        expect(response).to.deep.equal({ ok: 1 });

        return client.close();
      });
    });
  });
});

Motivations

I hadn’t found a solution in-the-wild that did this for me.

A while ago, The Code Barbarian had put up an excellent tutorial on using mongodb-topology-manager. So I cobbled together an end-to-end solution with the help of mongodb-prebuilt, a module which I learned about from my fiddlings with mockgoose.

I’d tried to use mockgoose within my professional App code for a while. It was hacky, but it was capable, and a reasonable drop-in.

To suit our in-house needs, I had to fork off a major refactor of its Connection management. I invested the time to build up a whole Test Suite around my changes, … but that was just gonna be too much churn by some random cantremember Guy to ever get merged back into the mainline by the maintainers ¯\_(ツ)_/¯ .

I have not tried out mongodb-memory-server. I’ve actually just learned about it from the mockgoose project’s swan-song README.

Hmm.

On the outside, it seems like it’s doing much of what my project does :open_mouth: and that’s my DRY face.

Have you heard of this thing called “mocking” ?

Oh, sure. But then you’re at the mercy of your fragile hacking of either the mongoose or mongodb APIs. Or both, because you can never have too much fragile.

My employer maintains a project whose Test Suite is 100% mocked DB interactions; it’s super hard to maintain – even says the guy :raised_hand: who had to write the thing in the first place. And who is going to justify paying down the technical debt to fix an inconvenient Test Suite?

No one, that’s who. Mocked database calls live forever.

It is so much better to test against a sandbox. The confidence of knowing the business logic produces reasonable queries, etc.

Alright. So just pre-install a MongoDB server.

Uh huh, I know. Every packaging system has one. MongoDB is even prevalent enough that every CI build system has some way to introduce the daemon as a sandbox binary.

But I really don’t want my Test Suites mucking with a locally-running server which might contain real data. mongodb-sandbox is built to seize up upon encountering a database that already contains Documents. And once its self-contained daemon shuts down, all of that data gets lost, which makes for a predictable clean slate every time.

There’s real value in having a run-anywhere self-contained solution for this.

Yet, a Sense of Ennui

You’d think I would be proud. My first public Open Source contribution to the npm biosphere!

But, not so much.

First,

MongoDB did a naming pattern change to the macOS binaries. Downloads failed in spectacular fashion. I pushed a fix for that back into mongodb-download.

So, subjectively, up to a point my module “just worked”. Until it didn’t.

Then,

CircleCI builds start failing at work. They’d upgraded their Ubuntu 14.04 “circleci/node:8” image, and in the process, libssl1.0.0 was no longer available.

Unfortunately, the pre-built mongod binaries are frozen in time with that dependency. The quick patch was to apt-get libssl1.0.0 as a prelude to our ‘test’ step. The public key importing too, Yeah, the whole bit.

Again, poking holes in the magic of my sandbox and its dependencies.

And, poking holes in the wisdom of pinning an employer’s code to my own personal project.

Also,

I had an existential quandry in my design. Should I download the mongod binary …

  • eagerly, during installation, or
  • lazily, upon first use?

The lazy path seemed more interesting, so I went down it – a decision I really regret, because

  • it takes work to be lazy. Under intermittent conditions, the first Test Case runs significantly longer than the others. The sandbox has to reach into the test framework and tweak the timeout allowance for the first Case. As of now, support is janky, incomplete, and definitely not lazy.

  • the first time I was riding (offline) on MUNI and tried to run a Test Suite which hasn’t cached the binary, I cursed my own goddamn name.

I will eventually refactor the thing to pay the download penalty up-front.

Plus,

I haven’t figured out how to get the bootstrapping to work in a truly parallel Test Suite environment, like the one that ava provides. The discrete mongod launches aren’t coordinated so so they end up in a failed race for ports.

Frankly, all of these shortcomings leave me feeling disappointed in my own work and the choices I’ve made in producing it.

Dispensing with the Fanfare

As of this writing, version 1.0.x is sealed in amber, and I shall endeavor to improve the module it over time.

I anticipate that the 1.1.x release will include

  • launch configuration for a Replica Set Topology, which the module has been designed to support since Day One

  • eager mongodb downloading

  • official Test Suite support & examples for at least one other framework

Ultimately, I hope this project can save at least one another Developer from having to home-rolling this solution, or something much like it, again.