Dookie lets you write MongoDB test fixtures in JSON or YAML with extra syntactic sugar (extended JSON, variables, imports, inheritance, etc.).
Note: Dookie requires Node >= 4.0.0. Dookie is not tested with nor expected to work with Node 0.x or io.js.
Dookie can be used either via require('dookie');
in Node.js, or from the
command line as an executable. Dookie's fundamental operations are:
- Push - optionally clear out a database and insert some data
- Pull - write the contents of a database to a file
Push is more interesting, so let's start with that. You can access the
push functionality with the require('dookie').push()
function, or
./node_modules/.bin/dookie push
from the command line.
Suppose you have a YAML file called file.yml
that looks like below.
people:
- _id:
# MongoDB extended JSON syntax
$oid: 561d87b8b260cf35147998ca
name: Axl Rose
- _id:
$oid: 561d88f5b260cf35147998cb
name: Slash
bands:
- _id: Guns N' Roses
members:
- Axl Rose
- Slash
Dookie can push this file to MongoDB for you.
co(function*() {
const fs = require('fs');
const yaml = require('js-yaml');
const contents = fs.readFileSync('./example/basic/file.yml');
const parsed = yaml.safeLoad(contents);
const mongodbUri = 'mongodb://localhost:27017/test';
// Insert data into dookie
// Or, at the command line:
// `dookie push --db test --file ./example/basic/file.yml`
yield dookie.push(mongodbUri, parsed);
// ------------------------
// Now that you've pushed, you should see the data in MongoDB
const db = yield mongodb.MongoClient.connect(mongodbUri);
const collections = (yield db.listCollections().toArray()).
map(v => v.name).filter(v => !v.startsWith('system.')).sort();
assert.equal(collections.length, 2);
assert.equal(collections[0], 'bands');
assert.equal(collections[1], 'people');
const people = yield db.collection('people').find().toArray();
people.forEach((person) => { person._id = person._id.toString() });
assert.deepEqual(people, [
{ _id: '561d87b8b260cf35147998ca', name: 'Axl Rose' },
{ _id: '561d88f5b260cf35147998cb', name: 'Slash' }
]);
const bands = yield db.collection('bands').find().toArray();
assert.deepEqual(bands, [
{ _id: `Guns N' Roses`, members: ['Axl Rose', 'Slash'] }
]);
})
Suppose you're a more advanced user and have some collections you want
to re-use between data sets. For instance, you may want a common collection
of users for your data sets. Dookie provides a $require
keyword just
for that. Suppose you have a file called parent.yml
:
$require: ./child.yml
bands:
- _id: Guns N' Roses
members:
- Axl Rose
This file does a $require
on child.yml
, which looks like this:
people:
- _id: Axl Rose
When you push parent.yml
, dookie will pull in the 'people' collection
from child.yml
as well.
co(function*() {
const filename = './example/$require/parent.yml';
const contents = fs.readFileSync(filename);
const parsed = yaml.safeLoad(contents);
const mongodbUri = 'mongodb://localhost:27017/test';
// Insert data into dookie
// Or, at the command line:
// `dookie push --db test --file ./example/basic/parent.yml`
yield dookie.push(mongodbUri, parsed, filename);
// ------------------------
// Now that you've pushed, you should see the data in MongoDB
const db = yield mongodb.MongoClient.connect(mongodbUri);
const people = yield db.collection('people').find().toArray();
assert.deepEqual(people, [{ _id: 'Axl Rose' }]);
const bands = yield db.collection('bands').find().toArray();
assert.deepEqual(bands, [
{ _id: `Guns N' Roses`, members: ['Axl Rose'] }
]);
})
You can also re-use objects using the $extend
keyword. Suppose each
person in the 'people' collection should have a parent pointer to the
band they're a part of. You can save yourself some copy/paste by using
$extend
:
$gnrMember:
band: Guns N' Roses
people:
- $extend: $gnrMember
_id: Axl Rose
- _id: Slash
$extend: $gnrMember
co(function*() {
const filename = './example/$extend.yml';
const contents = fs.readFileSync(filename);
const parsed = yaml.safeLoad(contents);
const mongodbUri = 'mongodb://localhost:27017/test';
// Insert data into dookie
// Or, at the command line:
// `dookie push --db test --file ./example/$extend.yml`
yield dookie.push(mongodbUri, parsed, filename);
// ------------------------
// Now that you've pushed, you should see the data in MongoDB
const db = yield mongodb.MongoClient.connect(mongodbUri);
const people = yield db.collection('people').find().toArray();
assert.deepEqual(people, [
{ band: `Guns N' Roses`, _id: 'Axl Rose' },
{ band: `Guns N' Roses`, _id: 'Slash' }
]);
})
Dookie also lets you evaluate code in your YAML. The code runs with the current document as the context.
people:
- _id: 0
firstName: Axl
lastName: Rose
name:
$eval: this.firstName + ' ' + this.lastName
co(function*() {
const filename = './example/$eval.yml';
const contents = fs.readFileSync(filename);
const parsed = yaml.safeLoad(contents);
const mongodbUri = 'mongodb://localhost:27017/test';
// Insert data into dookie
// Or, at the command line:
// `dookie push --db test --file ./example/$eval.yml`
yield dookie.push(mongodbUri, parsed, filename);
// ------------------------
// Now that you've pushed, you should see the data in MongoDB
const db = yield mongodb.MongoClient.connect(mongodbUri);
const people = yield db.collection('people').find().toArray();
assert.deepEqual(people, [
{ _id: 0, firstName: 'Axl', lastName: 'Rose', name: 'Axl Rose' }
]);
})
The above examples show how dookie can push()
data into MongoDB. Dookie
can also pull()
data out of MongoDB in JSON format. Why not just use
mongoexport or mongodump? Mongoexport can only export a single collection,
mongodump exports hard-to-read binary data, and neither can be run from
Node without .exec()
. Dookie lets you transfer whole databases in
a human readable format, and assert()
on the entire state of your
database in tests with ease.
co(function*() {
const mongodbUri = 'mongodb://localhost:27017/test';
// Insert data into dookie
// Or, at the command line:
// `dookie pull --db test --file ./output.json`
const json = yield dookie.pull(mongodbUri);
assert.deepEqual(Object.keys(json), ['people']);
assert.deepEqual(json.people, [
{ _id: 0, firstName: 'Axl', lastName: 'Rose', name: 'Axl Rose' }
]);
})