en-us pt-br

Automated UI testing with Cypress and Shippable

banner

Some background info before my recipe. If you’re in hurry, scroll down directly to Install Instructions.

Until now, UI automated testing has been a pain. Always relying on Selenium, and several other node packages, basically the setup I was using 2 years ago to test a react app had dependencies like this:

  • mocha (testing framework)
  • chai (for assertions)
  • chai-as-promised (cleaner assertions)
  • selenium-webdriver (for browser automation)
  • phantomjs-prebuilt (headless browser, to run the tests in the command line)
  • chromedriver (to run the tests using Chrome, good for debug)
  • coffee-script (cleaner sintax)

Not to mention that phantomjs was discontinued in favor of Chrome Headless.

Anyway, the whole shebang was really unstable: as Selenium is an external framework relying on a client/server communication, and phantomjs was not a real browser, several times our working tests will return false negatives.

Then, let’s say “hi!” to cypress and its incredible package dependency:

  • cypress

Yeap, that’s it: it’s a self contained node package, using Electron to run our tests with, in a headless way or with a friendly UI:

Cypress Test Runner

Install Instructions

Just run in your project folder:

npm install cypress --save-dev

After that, we’ll change some default behaviors:

  • we want nice commands to start cypress
  • we don’t want to keep cypress in our project root folder
  • we don’t want to save videos on test success
  • we don’t want cypress assets to be saved on git

Nice commands to start cypress

Open your package.json and add to the scripts section:

"scripts": {
  "cypress:open": "cypress open",
  "cypress": "cypress run"
},

So you’ll be able to call npm run cypress to run the tests in the command line and npm run cypress:open to open the test runner UI.

Change some defaults

By default, cypress will get installed to our project root folder, in a sub-folder named cypress. I moved mine to react/spec/system.

Additionaly I’ve changed my configuration to not save videos on test success and I’ve added a default value for the baseUrl (you can read more configuration values in the configuration documentation).

For all these changes, we need to create a cypress.json file in our project folder with the the following content:

{
  "baseUrl": "http://localhost",
  "fixturesFolder": "react/spec/system/fixtures",
  "integrationFolder": "react/spec/system/integration",
  "pluginsFile": "react/spec/system/plugins/index.js",
  "screenshotsFolder": "react/spec/system/screenshots",
  "supportFile": "react/spec/system/support/index.js",
  "videosFolder": "react/spec/system/videos",
  "video": false
}

Don’t save cypress assets on git

I still kept the cypress default behavior to take screenshots of testing failure when running them in the command line.

But I don’t want to save the screenshots (or even videos, if temporarily enabled) on git. So edit your .gitignore and add these lines:

# Cypress test results assets
react/spec/system/screenshots/*
react/spec/system/videos/*

Write a simple test

If you want more background on this, visit the cypress Write Your First Test page.

Create a file react/spec/system/integration/sample_spec.js with the following content:

describe('My First Test', function() {
  it('Visits Home', function() {
    cy.visit('/')
  })
})

Note that we’re opening the / page, which will visit the baseUrl defined in cypress.json.

As you can guess, visit opens a URL. Check the cypress API commands documentation about all available commands. You can create custom commands in react/spec/system/support/commands.js file.

Now check if our simple sample is working with npm run cypress:open and see the magic happening!

Continuous Integration with Shippable

Now let’s configure our Continuous Integration. I like Shippable for that, but any other CI tool will have a similar setup.

Why I like Shippable? Their Hosted SaaS is easy to configure (just a shippable.yml in your project), it has free or affordable tiers, builtin support for GitHub and BitBucket and works like a charm.

Anyway, like I said, probably any other CI tool will have similar setup. You can check in cypress CI integration page recipes for Jenkins, TrevisCI, [CircleCI)(https://circleci.com) and several others.

This post is not about to teach how to setup Shippable, but, in a quick overview, for a first setup, you need to follow these steps to enable in your project:

  1. Enable a project
  2. Configure build triggers
  3. Build your shippable.yml

Now, assuming you have Shippable working with a node_js setup, let’s change the ci section of shippable.yml to add our cypress tests:

ci:

  # Redirect the output to `/dev/null` due long outputs freezing the build
  - npm install --quiet >> /dev/null

  # Starting app server
  - rails server -d

  # cypress specs
  - npm run cypress

You can see that I’m using Shippable with Ubuntu in a rails application. If you’re using a different OS with Shippable, change the apt-get line to match your OS package manager. You can replace the line to start the web server too (rails server -d) by your application server.


Using ActionMailer with Localhost

banner

Some years ago I wrote a nice gist to setup an local Unix email delivering environment, for testing purposes.

In case you’re using Windows, I have one for you too.

Based on this, we’ll create an multi-environment mailer in Rails, using ActionMailer, which will send local emails using postfix for development, and can use any other configuration for production (including email services like SendGrid, SendinBlue, Amazon SES, Mandrill, Mailgun, etc).

I.e., locally we can test our Rails emails without rely on external services.

Before Start

Really, I’m assuming you have your postfix working like described in my gist.

Setup

Let’s start with a initializer, config/initializers/email.rb with the following content:

# config/initializers/email.rb
email_config = Rails.application.config_for :email

ActionMailer::Base.default_options = {
    from: email_config[:from]
}
ActionMailer::Base.raise_delivery_errors = email_config[:raise_delivery_errors]
ActionMailer::Base.smtp_settings = {
    user_name: email_config[:user_name],
    password: email_config[:password],
    domain: email_config[:domain],
    address: email_config[:address],
    port: email_config[:port],
    authentication: email_config[:authentication],
    enable_starttls_auto: email_config[:enable_starttls_auto],
    perform_deliveries: email_config[:perform_deliveries],
    delivery_method: email_config[:delivery_method]
}
unless email_config[:perform_deliveries]
  ActionMailer::Base.perform_deliveries = false
end

Then values for these configurations in /config/email.yml:

production:
  # Some prod config here

development:
  raise_delivery_errors: true
  domain: localhost
  perform_deliveries: true

test:
  delivery_method: test

Testing in Rails Console

Assuming you have an email like activation_instructions for a user, you can test using the deliver_now method in rails console:

    UserMailer.activation_instructions(@user).deliver_now

Caveats

Rails.application.config_for has no support for common section in the yml file.

If you’re using any other initialization method that has support for common section, it’s better:

  • clear values for authentication, user_name and password
  • change enable_starttls_auto to false and
  • make sure port value is 25

Example:

common:
  # Common config here

production:
  # Some prod specific config here

development:
  perform_deliveries: true
  port: 25
  authentication:
  user_name:
  password:
  enable_starttls_auto: false
  domain: localhost
  address: localhost

test:
  delivery_method: test

Additional Configurations

If you want more emails options to include in your initializer, like a default value for reply_to, please check all options in Action Mailer Configuration doc.


Super Nintendo history

banner

Exactly today is the release date for the Super Nintendo Classic, one of the most loved players’ consoles.

This version, similar to what happened to NES classic, is a miniaturized version, based on a Linux mini computer.

It has 20 games in the internal memory, including the never released Starfox 2, HDMI output and support for wireless gamepads.

SNES Classic SNES Classic

On its honor, I wrote a brief console story.


See all