June 6th, 2019
This is a basic guide on setting up end-to-end testing and continuous integration for a Meteor App w/ Cypress and Travis. End-to-end tests are "business logic" tests that ensure a program behaves correctly at the user level. Continuous integration is the practice of automating tests as part of our deployment process. With these two tools, we can push code changes with speed and confidence!
npm i --save-dev cypress start-server-and-test
Cypress generates some files (upon first run) that we want to move into the tests/
dir to avoid publishing w/ Meteor's web server.
# after running cypress once w/ "cypress run / cypress open"
mv cypress tests/cypress
# edit cypress.json:
{
"fixturesFolder": "tests/cypress/fixtures",
"integrationFolder": "tests/cypress/integration",
"pluginsFile": "tests/cypress/plugins/index.js",
"screenshotsFolder": "tests/cypress/screenshots",
"supportFile": "tests/cypress/support/index.js",
"videosFolder": "tests/cypress/videos",
"baseUrl": "http://localhost:3000"
}
Here's an example of a signup test. Cypress is awesome and definitely magical! Built-in timeouts and retries allow us to write our specs to closely mimic actual user behavior. Want to click something that contains the word "Register"? It's practically English.
describe("Signup / Login", () => {
before(() => {
// cypress support command (explained below)
cy.resetDatabase()
cy.visit("http://localhost:3000/signup")
})
it("should signup a new user", () => {
// submit signup form
cy.get('input[name="email"]').type("test-user@example.com")
cy.get('input[name="password"]').type("password")
cy.get('input[name="cPassword"]').type("password")
cy.contains("Register").click()
// should redirect to home page
cy.url().should("eq", "http://localhost:3000/")
// user exists and is now logged in
cy.window().then(win => {
// this allows accessing the window object within the browser
const user = win.Meteor.user()
expect(user).to.exist
expect(user.emails[0].address).to.equal("test-user@example.com")
})
})
})
We don't want our tests to be leaving around artifacts that will break later tests. Let's write a cypress support command (helper) to hard reset our database. I've included an optional step of exporting/restoring a DB dump that can serve as a fixtures file. Thanks to Mark Lynch on the meteor forums for this awesome trick!
// tests/cypress/support/commands.js
Cypress.Commands.add('resetDatabase', () =>
cy.exec('mongo mongodb://localhost:3001/meteor --eval "db.dropDatabase()"'),
// (optional) load in a db dump as fixtures
// can be generated w/ the following command:
// > mongodump --port 3001 --out ./tests/cypress/fixtures/test_db
cy.exec("mongorestore --port 3001 ./tests/cypress/fixtures/test_db");
);
// x.spec.js...
cy.resetDatabase();
To keep the dev environment flexible, we run tests in their own meteor directory, which will create it's own db instance. This can be accomplished by starting meteor with the following command in our test scripts:
# windows:
set METEOR_LOCAL_DIR=.meteor/test && meteor
# unix:
METEOR_LOCAL_DIR=.meteor/test meteor
test
to .meteor/.gitignore
"scripts": {
"start": "meteor",
"start:e2e": "set METEOR_LOCAL_DIR=.meteor/test && meteor",
"start:e2e:prod": "METEOR_LOCAL_DIR=.meteor/test meteor --production",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"test": "start-server-and-test start:e2e http://localhost:3000 cypress:open",
"test:prod": "start-server-and-test start:e2e:prod http://localhost:3000 cypress:run"
},
npm run test
for dev testing
npm run test:prod
for ci
Note: at this point, these scripts should run in their proper environment
Travis is configured w/ .travis.yml
. We specify our staging environments dependencies, how to cache them, what to test, and where to deploy.
language: node_js
node_js:
- 8.15.1
cache: npm
directories:
- "~/.cache"
- "~/.meteor"
- ".meteor/local"
before_install:
- curl https://install.meteor.com | /bin/sh
- npm install -g cypress
install:
- npm ci
script:
- npm run test:prod
deploy:
provider: heroku
app: appName
api_key:
secure: ######
package.json
travis encrypt $(heroku auth:token) --add deploy.api_key
install travis cli here
If all went well, we now have a continuous integration environment setup for our Meteor App. Overview of what it does:
Cypress has the awesome feature of recording videos of our integration tests. By connecting our CI test runs to the cypress dashboard, we are able to view video playback of them in action!
cypress open
)Settings tab > Record Key > [copy]
this becomes our new cypress:run
script in package.json
:
cypress run --record --key xxxxxx