Skip to content

Instantly share code, notes, and snippets.

@joelmukuthu
Created July 5, 2017 07:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joelmukuthu/02092afb36c89808b1f449d8ac792866 to your computer and use it in GitHub Desktop.
Save joelmukuthu/02092afb36c89808b1f449d8ac792866 to your computer and use it in GitHub Desktop.
unexpected-mongoose
const { Model, Types: { ObjectId, Document } } = require('mongoose');
module.exports = {
name: 'unexpected-mongan',
installInto: expect => {
expect.use(require('unexpected-set'));
expect.addType({
name: 'MongooseModel',
identify(val) {
return val instanceof Model;
},
equal(a, b, equal) {
return (
a.constructor.modelName === b.constructor.modelName &&
equal(a.toObject(), b.toObject())
);
}
});
expect.addType({
name: 'MongooseDocument',
identify(val) {
return val instanceof Document;
},
equal(a, b, equal) {
return equal(a.toObject(), b.toObject());
}
});
expect.addType({
name: 'MongooseModelArray',
base: 'array',
identify(val) {
return (
val &&
Array.isArray(val) &&
val.length &&
val.every(model => model instanceof Model)
);
}
});
expect.addType({
name: 'MongooseDocumentArray',
base: 'array',
identify(val) {
return (
val &&
Array.isArray(val) &&
val.length &&
val.every(doc => doc instanceof Document)
);
}
});
expect.addAssertion(
'<MongooseModel> to satisfy <object>',
(expect, subject, value) => {
return expect(subject.toObject(), 'to satisfy', value);
}
);
expect.addAssertion(
'<MongooseModel> to equal <object>',
(expect, subject, value) => {
return expect(subject.toObject(), 'to equal', value);
}
);
expect.addAssertion(
'<MongooseDocument> to satisfy <object>',
(expect, subject, value) => {
return expect(subject.toObject(), 'to satisfy', value);
}
);
expect.addAssertion(
'<MongooseDocument> to equal <object>',
(expect, subject, value) => {
return expect(subject.toObject(), 'to equal', value);
}
);
expect.addAssertion(
'<MongooseModelArray|MongooseDocumentArray> to [exhaustively] satisfy <array>',
(expect, subject, value) => {
return expect(
subject,
'with set semantics',
'to [exhaustively] satisfy',
value
);
}
);
expect.addAssertion(
'<MongooseModelArray|MongooseDocumentArray> to equal <array>',
(expect, subject, value) => {
return expect(subject, 'with set semantics', 'to equal', value);
}
);
}
};
@joelmukuthu
Copy link
Author

take these models:

const mongoose = require('mongoose');

const countrySchema = new mongoose.Schema({
  name: String,
  code: String
});
const continentSchema = new mongoose.Schema({
  name: String,
  countries: [ countrySchema ]
});

const Country = mongoose.model('Country', countrySchema);
const Continent = mongoose.model('Continent', continentSchema);

then in a test, add countries to the continent and assert that those countries were added:

const europe = new Continent({ name: 'Europe' });

europe.countries.push(
  new Country({ name: 'Sweden', code: 'sv' }},
  new Country({ name: 'Denmark', code: 'dk' }}
);

expect(europe, 'to satisfy', {
  countries: [ // here the order of countries shouldn't matter
    { name: 'Denmark' },
    { name: 'Sweden' }
  ]
});

What i'm trying to get to work is the <MongooseDocumentArray> to satisfy <array> assertion on the countries field. I can't guarantee the order of items in the array so I just want to use with set semantics to ensure that those items are there, regardless of the order in which they are.

The problem is that unexpected does not identify the array of countries as a MongooseDocumentArray because in asserting that europe satisfies the RHS object, it first uses the <MongooseModel> to satisfy <object> assertion, thereby calling europe.toObject() which means that by the time it's checking that europe.countries satisfies the RHS array, europe.countries is not a MongooseDocumentArray anymore, but just an array of plain objects. FYI, calling toObject on a mongoose model has the effect of calling toObject on any nested documents, in this case the documents in countries.

Is there a better way to do this, besides expect(europe.countries, 'to satisfy', [])?

PS: I'm aware that the array types might be bad for performance but I'm ignoring that for now :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment