here's an example of how I do it.
assume a `dbInit()` function somewhere that initialises the database for
your app as per usual and returns a promise. I use sequelize but the same
ideas apply to any ORM.
add a test_helper.js file
import chai from 'chai'
import sinon from 'sinon' // eslint-disable-line no-unused-vars
import sinonChai from 'sinon-chai'
import chaiString from 'chai-string'
import faker from 'faker'
import prepare from 'mocha-prepare'
import dbInit from '../../src/utils/db/dbInit'
import models from '../../src/models'
chai.use(sinonChai)
chai.use(chaiString)
prepare((done) => {
dbInit().then(() => {
models.sequelize.sync({ force: true }).then(() => {
done()
})
}, done)
})
invoke your test from `npm run test:db`.
"test:db": "find ./test/db -name '*_spec.js' | NODE_ENV=test xargs mocha
--compilers js:babel-core/register --require ./test/db/test_helper.js
--bail",
then my `user_spec.js`
import { expect } from 'chai'
import models from '../../../src/models'
import dummyData from '../../utils/dummyData'
import Factories from '../../utils/factories'
const { User } = models
const { makeUser } = Factories(models)
describe('User', () => {
afterEach((done) => {
User.destroy({ where: {} }).then(() => done())
})
describe('create', () => {
describe('given basic valid data', () => {
const userData = dummyData.userData()
it('creates a user', (done) => {
User.create(userData, { include: [{ all: true }] }).then((user) => {
const userJS = user.get({ plain: true })
expect(userJS).to.have.property('email')
expect(userJS.email).to.equal(userData.email.toLowerCase())
expect(userJS).to.have.property('passwordDigest')
expect(userJS).to.have.property('keys')
expect(userJS.keys).to.be.empty
expect(user.authenticate('password')).to.equal(user)
done()
}, done)
})
})
describe('given bad email address', () => {
const userData = dummyData.userData({ email: 'not an email address' })
it('fails with a validation error', (done) => {
User.create(userData).then((ignore) => {
done(ignore) // will error if ignore is not null
}, (err) => {
expect(err).to.exist
expect(err.message).to.equal('Validation error: Validation
isEmail failed')
done()
})
})
})
})
describe('#setMagicToken', () => {
let user
beforeEach((done) => {
makeUser().then((u) => {
user = u
done()
}, done)
})
it('can set a new magic token', (done) => {
expect(user.magicToken).to.be.null
user.setMagicToken().then((token) => {
expect(token).to.not.be.null
expect(token).to.not.be.undefined
expect(user.magicToken).to.equal(token)
User.findOne({ where: { magicToken: token } }).then((foundUser) => {
expect(foundUser).to.not.be.null
expect(foundUser).to.not.be.undefined
expect(foundUser).to.have.property('email', user.email)
done()
}, done)
}, done)
})
})
})
the `dummyData.js` is just
import faker from 'faker'
const credentials = fields => ({
email: faker.internet.exampleEmail(),
password: faker.internet.password(),
...fields
})
const userData = fields => ({
email: faker.internet.exampleEmail(),
password: 'password',
keys: [],
...fields
})
module.exports = {
credentials,
userData
}
and factories.js is just
import { userData } from './dummyData'
const Factories = (models) => {
const { User } = models
const makeUser = fields => User.create(userData(fields))
return {
makeUser
}
}
module.exports = Factories
Your database config ought to allow multiple environments, ala
{
"development": {
"username": "docker",
"password": "docker",
"database": "my_project-development",
"host": "localhost",
"dialect": "postgres"
},
"test": {
"username": "docker",
"password": "docker",
"database": "my_project-test",
"host": "localhost",
"dialect": "postgres"
},
"production": {
"username": "docker",
"password": "docker",
"database": "my_project-production",
"host": "localhost",
"dialect": "postgres"
}
}
So when your tests are running you'll not touch your dev db. When you push
to production the production environment settings are normally baked in as
ENV settings so they get ignored from here.
During development and test (or if I am firing it up in production mode on
my laptop) I run my database within docker rather than on the bare metal.
docker-compose.yml
version: '2.1'
# see https://github.com/sameersbn/docker-postgresql
volumes:
# We'll define a volume that will store the data from the postgres
databases:
postgres-data:
driver: local
services:
# Our PostgreSQL service:
postgres:
image: postgres:9.6.2
ports:
- 5432:5432
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: "exit 0"
environment:
POSTGRES_USER: docker
POSTGRES_PASSWORD: docker
put all that together and you have a nice modular system for connecting to
the database, then running discreet tests.
It would be nice if there was an equiv of DatabaseCleaner (for Ruby's
ActiveRecord and RSpec) for Node and Mocha.
Cheers
Dave
On Wednesday, 5 April 2017 04:54:55 UTC+9:30, Kevin B wrote:
>
> I'm having a heck of a time trying to figure out how to write unit tests
> for my projects that rely on mysql. I want to be able to roll back my test
> database so I can run each test in a fresh environment but every time I try
> to write a simple string of commands to delete, create, populate the
> database so it's in a known state, I run into issues. I can't seem to get
> the database to create the same table after it has recently been deleted.
> Even if I use Promises to mitigate syncranousness of JS the table just
> refuses to be recreated.Any ideas on how to best setup for unit testing db
> code?
>
--
Job board: http://jobs.nodejs.org/
New group rules:
https://gist.github.com/othiym23/9886289#file-moderation-policy-md
Old group rules:
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
---
You received this message because you are subscribed to the Google Groups
"nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/nodejs/da26af39-6286-46c2-8bb5-29e08cb4a1bc%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.