Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My mocha tests work separately, but fail when run all at once

This probably has to do with asynchronous code, but I'm not sure what. The both pass when I run them apart from each other: mocha test/models.coffee and mocha test/login.coffee

But describe 'saving another user with same username', -> fails when I run the tests together with npm test. I imagine the database gets cleared as it's running that step so it saves instead of producing an error because it's supposed to be a unique value.

ALSO: I am extremely new at this software testing stuff, if anyone has any tips, or wants to criticize my blatant mistakes, feel free to email me (check my profile)

Thanks!!

Here are my two test files (coffeescript):

/test/models.coffee

should = require 'should'
{User} = require '../models'

# function to find our test user, John Egbert
findJohn = (cb) ->
  User.findOne {'public.username': 'john'}, (err, john) ->
    throw err if err
    cb(john)

before (done) ->

  # Drop all users from database
  User.collection.drop()

  # Create our test user, his username is John Egbert
  User.create
    password: 'test123'
    public: {username: 'john'}, done

describe 'create user', ->
  it 'should create a document that can be found', (done) ->
    findJohn (user) ->
      should.exist user
      done()

describe 'user password', ->
  it 'should NOT be stored in plaintext', (done) ->
    findJohn (user) ->
      user.password.should.not.eql 'test123'
      done()

  it 'should return true for matching password', (done) ->
    findJohn (user) ->
      user.comparePassword 'test123', (err, isMatch) ->
        isMatch.should.eql true
        done()

  it 'should return false for non-matching password', (done) ->
    findJohn (user) ->
      user.comparePassword 'wrong_password', (err, isMatch) ->
        isMatch.should.not.eql true
        done()

describe 'saving another user with same username', ->
  it 'should produce an error', (done) ->
    User.create public: {username: 'john'}, (err) ->
      should.exist err
      done()

/test/login.coffee:

should = require 'should'
{User} = require '../models'
login = require '../services/login'

before (done) ->

  # Drop all users from database
  User.collection.drop()

  # Create our test user, his username is John Egbert
  User.create
    password: 'test123'
    public: {username: 'john'}, done

describe 'login', ->
  it 'should return true for an existing username/password combo', (done) ->
    login username: 'john', password: 'test123', (err, loggedIn) ->
      should.not.exist(err)
      loggedIn.should.be.true
      done()

  it 'should return false for a bad username/password combo', (done) ->
    login username: 'john', password: 'wrong_pass', (err, loggedIn) ->
      should.not.exist(err)
      loggedIn.should.be.false
      done()

And /models.coffee

fs = require 'fs'
path = require 'path'
mongoose = require 'mongoose'

#Connect to mongodb
#TODO: env variable to choose production/development/testing databases
mongoose.connect 'localhost', 'siglr'

models = {}

for file in fs.readdirSync './schemas'
  if path.extname(file) is '.coffee'
    modelName = path.basename file, '.coffee'
    schema = require "./schemas/#{modelName}"
    models[modelName] = mongoose.model modelName, schema

# key is model name, value is actual mongoose model
module.exports = models
like image 856
nak Avatar asked Oct 21 '22 20:10

nak


2 Answers

Here's one thing that could be creating race conditions that will make you crazy. You aren't passing a callback when dropping the users collection. I can't say for sure if this will ever cause a problem such as the test user being inserted before the collection gets dropped, but this pattern of failing to use callbacks properly to wait for asynchronous operations to complete is a recipe for your program to misbehave in hard-to-debug ways. Try this:

before (done) ->

    # Drop all users from database
    User.collection.drop (err) ->

        # Create our test user, his username is John Egbert
        User.create
            password: 'test123'
            public: {username: 'john'}, done

2nd suggestion: put console.log statements at the beginning (1st statement) of every describe/before/it and another one immediately prior to calling done() and see if they appear in exactly the order you expect.

like image 148
Peter Lyons Avatar answered Oct 27 '22 11:10

Peter Lyons


I found that for some reason the public.username index is removed when running the tests together with npm test. But was maintained when running each test alone.

I changed the following in the test/models.coffee:

  # Create our test user, his username is John Egbert
  User.create
    password: 'test123'
    public: {username: 'john'}, done

To the following:

  # Create our test user, his username is John Egbert
  User.create
    password: 'test123'
    public: {username: 'john'}, ->
      User.collection.ensureIndex { 'public.username': 1 }, { unique: true }, (err) ->
        throw err if err
        done()

...which calls ensureIndex an internal mongoose function which is called when the model is compiled initially, but is removed for some reason during the test's life-cycle.

Using Mongoose 3.5.4

like image 44
nak Avatar answered Oct 27 '22 09:10

nak