Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of using Mockgoose with AVA #3

Closed
sibelius opened this issue Aug 23, 2016 · 4 comments
Closed

Example of using Mockgoose with AVA #3

sibelius opened this issue Aug 23, 2016 · 4 comments

Comments

@sibelius
Copy link

I want to run tests in parallel that needs a mongo database using ava

My current problem is that I need a different database for each test as I don't want one test to influence into another

this is my SO question - http://stackoverflow.com/questions/39100051/how-to-run-parallel-tests-with-ava-and-mongoose

this is a good discussion as well - avajs/ava#472

Can I connect into different databases using mockgoose?

this is a sample code that I'm trying to use it

import test from 'ava';
import uid2 from 'uid2';

function connectMongo(config) {
  return new Promise((resolve, reject) => {
    mongoose.Promise = global.Promise;
    mongoose.connection
      .on('error', error => reject(error))
      .on('close', () => console.log('Database connection closed.'))
      .once('open', () => resolve(mongoose.connections[0]));

    mongoose.connect(config);
  });
}

test.beforeEach(async t => {
  await mockgoose(mongoose);
  const tempDb = `mongodb://localhost/${uid2(20)}`; // generate random db name

  //connect to database
  const info = await connectMongo(tempDb);
  console.log(`Connected to ${info.host}:${info.port}/${info.name}`);
});

test.afterEach(t => {
  mockgoose.reset(err => {
    if (err) t.fail(err)
  });
});

test('save() should not save an empty product', async t => {
  try {
    let sample = {};
    let product = new Product(sample);
    await product.save();
  } catch(err) {
    t.is(err.name, 'ValidationError');
  }
});

this is the error that I've got running the master of this repo:

failed with "uri.indexOf is not a function"
      checkReplicaSetInUri (server/node_modules/mongoose/lib/index.js:115:30)
    Mongoose.connect (server/node_modules/mongoose/lib/index.js:237:40)
    Mongoose.mongoose.connect (server/node_modules/mockgoose/Mockgoose.js:82:33)
@nfcampos
Copy link

nfcampos commented Aug 23, 2016

I did try to achieve that and didn't manage to. in the end it comes down to the fact that mongoose itself can only be connected to one db at a time. so by reconnecting on beforeEach you end up removing the connection to the db while another test is maybe writing to it, which makes for some pretty confusing bugs with stack traces deep inside mongoose itself.

the way to achieve this would be to have different mongoose instances for each test, which seeing as all tests in the same file run in the same process is not straightforward at all. it might be possible but I wouldn't bet on it.

@nfcampos
Copy link

nfcampos commented Aug 23, 2016

on further investigation, it should actually possible to do something like this, the awkward part will be getting a reference to the models inside each test
here goes:

import test from 'ava'
import mongoose from 'mongoose'
import mockgoose from 'mockgoose'
// i'm assuming this models file is a file that creates or imports all the mdoels you want to use in your test here 
import models from '../../models/'

test.beforeEach(async t => {
  // mongoose exposes the constructor for the mongoose object
  const mongooseInstance = new mongoose.Mongoose
  // the default export is simply an instance of that constructor

  // mock this freshly created instance
  await mockgoose(mongooseInstance)

  // from my tests it works even if all instances "connect" to the same url
  const url = `mongodb://2`
  // connect this fresh instance
  await mongooseInstance.connect(url)

  // recreate the models on this new mongoose instance
  Object.keys(mongoose.models).forEach(name => {
    const model = mongoose.models[name]
    mongooseInstance.model(name, model.schema)
  })

  // expose the mongoose instance to the test
  t.context.mongoose = mongooseInstance
})

test('insert', async t => {
  // each test must use its own mongoose instance and retrieve the models from there
  const Task = t.context.mongoose.model('Task')
  await new Task({
    session_id: 'abc',
    name: 'abc'
  }).save()

  const tasks = await Task.find({})

  t.is(tasks.length, 1)
})

test('list', async t => {
  // each test must use its own mongoose instance and retrieve the models from there
  const Task = t.context.mongoose.model('Task')

  // wait 2s to make sure the 'insert' test has inserted the model
  await wait(2000)

  const tasks = await Task.find({})

  // the model inserted in the other test is not present in this one :)
  t.is(tasks.length, 0)
})

function wait(timeout) {
  return new Promise((resolve) => setTimeout(resolve, timeout))
}

the gotcha here is that inside each test you really have to use that test's mongoose instance, so if you're trying to test a server that uses the models things might get trickier, but for directly testing the models it should work.
@sibelius let me know if this works for you

@sibelius
Copy link
Author

This is pretty awesome but very hack really thanks

I need to test a koa server (endpoint test) that will use these models

I'll let u know if I figure it out how to handle this case

@sibelius
Copy link
Author

I've decided to move to jest, and use snapshot test on my mongoose model tests

it just works

thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants