├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── example ├── manual.js └── test.js ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | services: 5 | - mongodb 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Elliot Foster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | testwatch: 2 | ./node_modules/.bin/nodemon -L -d 0 -w . --exec make test 3 | 4 | test: 5 | ./node_modules/.bin/mocha --recursive --reporter list test/index.js 6 | ./node_modules/.bin/mocha --recursive --reporter list example/test.js 7 | ./node_modules/.bin/mocha --recursive --reporter list example/manual.js 8 | 9 | clean: 10 | rm -rf node_modules 11 | 12 | install: 13 | npm install 14 | 15 | .PHONY: clean install test testwatch 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mocha-mongoose 2 | ============== 3 | 4 | Test helpers for using mongoose with mocha. 5 | 6 | See the example spec (copied below) for more details. 7 | 8 | [![Travis-CI Build Status](https://secure.travis-ci.org/elliotf/mocha-mongoose.png)](http://travis-ci.org/elliotf/mocha-mongoose) 9 | [![Drone.io Build Status](https://drone.io/github.com/elliotf/mocha-mongoose/status.png)](https://drone.io/github.com/elliotf/mocha-mongoose/latest) 10 | 11 | ## Installation 12 | 13 | 1. install via npm 14 | 15 | $ npm install mocha-mongoose 16 | 17 | 1. require mocha-mongoose in your spec helper (easier) or in each spec file 18 | 19 | require('mocha-mongoose')('mongodb://your-mongodb-url-here'); 20 | 21 | 1. mocha-mongoose will automatically clear all of your collections before each spec run 22 | 1. optionally provide a `skip` option to tell mocha-mongoose not to clear specific collections. 23 | require('mocha-mongoose')(dbURI, { skip: ['collectionname1', 'collectionname2'] }); 24 | 25 | ## Example usage of automatically clearing the DB between specs: 26 | 27 | This is a copy of example/test.js 28 | 29 | ```javascript 30 | var dbURI = 'mongodb://localhost/demo-app-clearing-db' 31 | , should = require('chai').should() 32 | , mongoose = require('mongoose') 33 | , Dummy = mongoose.model('Dummy', new mongoose.Schema({a:Number})) 34 | , clearDB = require('mocha-mongoose')(dbURI) 35 | ; 36 | 37 | describe("Example spec for a model", function() { 38 | beforeEach(function(done) { 39 | if (mongoose.connection.db) return done(); 40 | 41 | mongoose.connect(dbURI, done); 42 | }); 43 | 44 | it("can be saved", function(done) { 45 | new Dummy({a: 1}).save(done); 46 | }); 47 | 48 | it("can be listed", function(done) { 49 | new Dummy({a: 1}).save(function(err, model){ 50 | if (err) return done(err); 51 | 52 | new Dummy({a: 2}).save(function(err, model){ 53 | if (err) return done(err); 54 | 55 | Dummy.find({}, function(err, docs){ 56 | if (err) return done(err); 57 | 58 | // without clearing the DB between specs, this would be 3 59 | docs.length.should.equal(2); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | }); 65 | 66 | it("can clear the DB on demand", function(done) { 67 | new Dummy({a: 5}).save(function(err, model){ 68 | if (err) return done(err); 69 | 70 | clearDB(function(err){ 71 | if (err) return done(err); 72 | 73 | Dummy.find({}, function(err, docs){ 74 | if (err) return done(err); 75 | 76 | console.log(docs); 77 | 78 | docs.length.should.equal(0); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | }); 85 | ``` 86 | 87 | ## Example usage of manually clearing the DB: 88 | 89 | This is a copy of example/manual.js 90 | 91 | ```javascript 92 | var dbURI = 'mongodb://localhost/demo-app-clearing-db' 93 | , expect = require('chai').expect 94 | , mongoose = require('mongoose') 95 | , Dummy = mongoose.model('Dummy', new mongoose.Schema({a:Number})) 96 | , clearDB = require('mocha-mongoose')(dbURI, {noClear: true}) 97 | ; 98 | 99 | describe("Example spec for a model", function() { 100 | before(function(done) { 101 | if (mongoose.connection.db) return done(); 102 | 103 | mongoose.connect(dbURI, done); 104 | }); 105 | 106 | before(function(done) { 107 | clearDB(done); 108 | }); 109 | 110 | it("can be saved", function(done) { 111 | Dummy.create({a: 1}, done); 112 | }); 113 | 114 | it("can save another", function(done) { 115 | Dummy.create({a: 2}, done); 116 | }); 117 | 118 | it("can be listed", function(done) { 119 | Dummy.find({}, function(err, models){ 120 | expect(err).to.not.exist; 121 | expect(models).to.have.length(2); 122 | 123 | done(); 124 | }); 125 | }); 126 | 127 | it("can clear the DB on demand", function(done) { 128 | Dummy.count(function(err, count){ 129 | expect(err).to.not.exist; 130 | expect(count).to.equal(2); 131 | 132 | clearDB(function(err){ 133 | expect(err).to.not.exist; 134 | 135 | Dummy.find({}, function(err, docs){ 136 | expect(err).to.not.exist; 137 | 138 | expect(docs.length).to.equal(0); 139 | done(); 140 | }); 141 | }); 142 | }); 143 | }); 144 | }); 145 | ``` 146 | -------------------------------------------------------------------------------- /example/manual.js: -------------------------------------------------------------------------------- 1 | // manually clearing the DB 2 | var dbURI = 'mongodb://localhost/demo-app-clearing-db' 3 | , expect = require('chai').expect 4 | , mongoose = require('mongoose') 5 | , Dummy = mongoose.model('Dummy', new mongoose.Schema({a:Number})) 6 | 7 | , clearDB = require('../index')(dbURI, {noClear: true}) 8 | // Normally, this is: 9 | //, clearDB = require('mocha-mongoose')(dbURI, {noClear: true}) 10 | ; 11 | 12 | describe("Example spec for a model", function() { 13 | before(function(done) { 14 | if (mongoose.connection.db) return done(); 15 | 16 | mongoose.connect(dbURI, done); 17 | }); 18 | 19 | before(function(done) { 20 | clearDB(done); 21 | }); 22 | 23 | it("can be saved", function(done) { 24 | Dummy.create({a: 1}, done); 25 | }); 26 | 27 | it("can save another", function(done) { 28 | Dummy.create({a: 2}, done); 29 | }); 30 | 31 | it("can be listed", function(done) { 32 | Dummy.find({}, function(err, models){ 33 | expect(err).to.not.exist; 34 | expect(models).to.have.length(2); 35 | 36 | done(); 37 | }); 38 | }); 39 | 40 | it("can clear the DB on demand", function(done) { 41 | Dummy.count(function(err, count){ 42 | expect(err).to.not.exist; 43 | expect(count).to.equal(2); 44 | 45 | clearDB(function(err){ 46 | expect(err).to.not.exist; 47 | 48 | Dummy.find({}, function(err, docs){ 49 | expect(err).to.not.exist; 50 | 51 | expect(docs.length).to.equal(0); 52 | done(); 53 | }); 54 | }); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /example/test.js: -------------------------------------------------------------------------------- 1 | var dbURI = 'mongodb://localhost/demo-app-clearing-db' 2 | , should = require('chai').should() 3 | , mongoose = require('mongoose') 4 | , Dummy = mongoose.model('Dummy', new mongoose.Schema({a:Number})) 5 | 6 | , clearDB = require('../index')(dbURI) 7 | // Normally, this is: 8 | //, clearDB = require('mocha-mongoose')(dbURI) 9 | ; 10 | 11 | describe("Example spec for a model", function() { 12 | beforeEach(function(done) { 13 | if (mongoose.connection.db) return done(); 14 | 15 | mongoose.connect(dbURI, done); 16 | }); 17 | 18 | it("can be saved", function(done) { 19 | new Dummy({a: 1}).save(done); 20 | }); 21 | 22 | it("can be listed", function(done) { 23 | new Dummy({a: 1}).save(function(err, model){ 24 | if (err) return done(err); 25 | 26 | new Dummy({a: 2}).save(function(err, model){ 27 | if (err) return done(err); 28 | 29 | Dummy.find({}, function(err, docs){ 30 | if (err) return done(err); 31 | 32 | // without clearing the DB between specs, this would be 3 33 | docs.length.should.equal(2); 34 | done(); 35 | }); 36 | }); 37 | }); 38 | }); 39 | 40 | it("can clear the DB on demand", function(done) { 41 | new Dummy({a: 5}).save(function(err, model){ 42 | if (err) return done(err); 43 | 44 | clearDB(function(err){ 45 | if (err) return done(err); 46 | 47 | Dummy.find({}, function(err, docs){ 48 | if (err) return done(err); 49 | 50 | docs.length.should.equal(0); 51 | done(); 52 | }); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var url = require('url') 2 | , client = require('mongodb').MongoClient 3 | ; 4 | 5 | var beforeEachRegistered = false; 6 | var afterHookRegistered = false; 7 | 8 | module.exports = function(uriString, options) { 9 | options = options || {}; 10 | 11 | if (typeof uriString == 'function') { 12 | throw new Error("Module being called to clean the db. Please call the module with a mongodb url."); 13 | } 14 | 15 | if (!uriString) { 16 | console.warn("!WARNING: no mongodb url provided. Defaulting to mongodb://localhost/test"); 17 | uriString = 'mongodb://localhost/test'; 18 | } 19 | 20 | var db = null; 21 | 22 | if (!options.noClear && !beforeEachRegistered) { 23 | if ('function' == typeof beforeEach && beforeEach.length > 0) { 24 | // we're in a test suite that hopefully supports async operations 25 | beforeEach(clearDB); 26 | beforeEachRegistered = true; 27 | } 28 | } 29 | 30 | if (!options.noClear && !afterHookRegistered) { 31 | if ('function' == typeof after && after.length > 0) { 32 | // we're in a test suite that hopefully supports async operations 33 | after(closeDB); 34 | afterHookRegistered = true; 35 | } 36 | } 37 | 38 | return function(done) { 39 | clearDB(done); 40 | }; 41 | 42 | function clearDB(done) { 43 | if (db) return clearCollections(done); 44 | 45 | client.connect(uriString, function(err, newDb){ 46 | if (err) return done(err); 47 | 48 | db = newDb; 49 | 50 | clearCollections(done); 51 | }); 52 | } 53 | 54 | function clearCollections(done) { 55 | db.collections(function(err, collections){ 56 | if (err) return done(err); 57 | 58 | var todo = collections.length; 59 | if (!todo) return done(); 60 | 61 | collections.forEach(function(collection){ 62 | if (collection.collectionName.match(/^system\./)) return --todo; 63 | if (options.skip instanceof Array && options.skip.indexOf(collection.collectionName) > -1) return --todo; 64 | 65 | collection.remove({},{safe: true}, function(){ 66 | if (--todo === 0) done(); 67 | }); 68 | }); 69 | }); 70 | } 71 | 72 | function closeDB() { 73 | db.close(); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Elliot Foster ", 3 | "name": "mocha-mongoose", 4 | "description": "Test helpers for using mongodb with mocha", 5 | "version": "1.2.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/elliotf/mocha-mongoose" 9 | }, 10 | "scripts": { 11 | "test": "make test" 12 | }, 13 | "license": "MIT", 14 | "peerDependencies": { 15 | "mocha": ">=1.0.0", 16 | "mongodb": ">=1.0.0" 17 | }, 18 | "devDependencies": { 19 | "chai": ">=1.0.0", 20 | "mocha": ">= 1.0.0", 21 | "mongodb": ">=1.0.0", 22 | "mongoose": ">=3.0.0", 23 | "nodemon": "~0" 24 | }, 25 | "optionalDependencies": { 26 | }, 27 | "engines": { 28 | "node": "*", 29 | "npm": ">= 1.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // make it extremely unlikely that this test unintentionally drops someone's DB 2 | var uniqueId = 'c90b6960-0109-11e2-9595-00248c45df8a' 3 | , dbURI = 'mongodb://localhost/mongodb-wiper-test-' + uniqueId 4 | , expect = require('chai').expect 5 | , mongoose = require('mongoose') 6 | , Dummy = mongoose.model('Dummy', new mongoose.Schema({a:Number})) 7 | , ImmortalDummy = mongoose.model('ImmortalDummy', new mongoose.Schema({a:Number})) 8 | ; 9 | 10 | 11 | describe("clearDB", function() { 12 | var clearDB, options; 13 | 14 | beforeEach(function(done) { 15 | options = {noClear: true}; 16 | 17 | if (mongoose.connection.db) return done(); 18 | mongoose.connect(dbURI, done); 19 | }); 20 | 21 | describe(".clearDB", function() { 22 | beforeEach(function() { 23 | clearDB = require('../index')(dbURI, options); 24 | }); 25 | 26 | it("is available", function() { 27 | expect(clearDB).to.be.a('function'); 28 | }); 29 | 30 | it("clears the database when called", function(done) { 31 | Dummy.create({a: 1}, function(err){ 32 | if (err) return done(err); 33 | 34 | Dummy.find({}, function(err, docs){ 35 | expect(err).to.not.exist; 36 | expect(docs).to.have.length.above(0); 37 | clearDB(function(err){ 38 | Dummy.find({}, function(err, docs){ 39 | expect(err).to.not.exist; 40 | expect(docs).to.have.length(0); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | }); 46 | }); 47 | 48 | describe("when required with the skip option", function() { 49 | beforeEach(function(done) { 50 | options.skip = ['immortaldummies'] 51 | clearDB = require('../index')(dbURI, options); 52 | 53 | Dummy.create({a: 1}, function(err){ 54 | if (err) return done(err); 55 | 56 | ImmortalDummy.create({a: 1}, function(err){ 57 | if (err) return done(err); 58 | 59 | done(); 60 | }); 61 | }); 62 | }); 63 | 64 | it("does not clear the skipped collections", function(done) { 65 | clearDB(function(err){ 66 | Dummy.find({}, function(err, docs){ 67 | expect(err).to.not.exist; 68 | expect(docs).to.have.length(0); 69 | 70 | ImmortalDummy.find({}, function(err, docs){ 71 | expect(err).to.not.exist; 72 | expect(docs).to.have.length.above(0); 73 | 74 | done(); 75 | }); 76 | }); 77 | }); 78 | }); 79 | }); 80 | }); 81 | 82 | describe("when called with a callback instead of a url", function() { 83 | it("throws an error", function(done) { 84 | var err; 85 | try { 86 | require('../index')(done, options); 87 | } catch(e) { 88 | err = e; 89 | } finally { 90 | expect(err).to.exist; 91 | expect(err.message).to.match(/being called to clean/); 92 | expect(err.message).to.match(/with a mongodb url/); 93 | done(); 94 | } 95 | }); 96 | }); 97 | 98 | describe("inside mocha", function() { 99 | function itAllowsNormalUse() { 100 | it("allows normal db use", function(done) { 101 | new Dummy({a: 2}).save(function(err){ 102 | if (err) return done(err); 103 | 104 | Dummy.find({}, function(err,docs){ 105 | if (err) return done(err); 106 | 107 | expect(docs.length).to.equal(1); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | } 113 | 114 | describe("when required with the noClear option", function() { 115 | beforeEach(function() { 116 | clearDB = require('../index')(dbURI, options); 117 | }); 118 | 119 | itAllowsNormalUse(); 120 | 121 | it("does not clear out the DB automatically", function(done) { 122 | Dummy.find({}, function(err, docs){ 123 | if (err) return done(err); 124 | expect(docs.length).to.equal(1); 125 | 126 | clearDB(done); 127 | }); 128 | }); 129 | }); 130 | 131 | describe("when required without the noClear option", function() { 132 | beforeEach(function() { 133 | clearDB = require('../index')(dbURI); 134 | }); 135 | 136 | itAllowsNormalUse(); 137 | 138 | it("automatically empties the db between specs", function(done) { 139 | Dummy.find({}, function(err, docs){ 140 | if (err) return done(err); 141 | expect(docs.length).to.equal(0); 142 | done(); 143 | }); 144 | }); 145 | }); 146 | }); 147 | 148 | describe("system collections", function() { 149 | it("does not clear out system collections", function() { 150 | // well, this kind of sucks... How do I test this? 151 | }); 152 | }); 153 | }); 154 | --------------------------------------------------------------------------------