Larry Price

And The Endless Cup Of Coffee

Javascript Testing With Capybara and Cucumber

| Comments

In the past, I had written off testing the Javascript in my Sinatra apps as being not worth the pain of setting up. That was pretty naïve of me, as setting up web drivers in Capybara is actually pretty easy.

For this post, I assume you already have a capybara-cucumber project set up. If you need help setting up your first tests, consider checking out some of my other blog posts on the subject.

Selenium

Selenium is the default Javascript driver for capybara. To install either run gem install selenium-webdriver or toss selenium-webdriver into your Gemfile and run bundle install.

If you want to run all of your tests with Javascript enabled, you can change the default driver in your env.rb file. For example:

env.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ENV['RACK_ENV'] = 'test'

require_relative '../../../web'

require 'selenium-webdriver'
require 'capybara/cucumber'
require 'rspec'

Capybara.app = Ollert
Capybara.default_wait_time = 10

# run all tests using Javascript
Capybara.default_driver = :selenium

World do
  Ollert.new
  Mongoid.purge!
end

Using Selenium means that your tests will be running using Firefox. Unfortunately, this makes them much, much slower than when you were running the tests using rspec. What I recommend is to limit yourself to only use the Javascript driver when you need to. To accomplish this, we change our env.rb file as such:

env.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ENV['RACK_ENV'] = 'test'

require_relative '../../../web'

require 'selenium-webdriver'
require 'capybara/cucumber'
require 'rspec'

Capybara.app = Ollert
Capybara.default_wait_time = 10

# use the following web driver to run tests
Capybara.javascript_driver = :selenium

World do
  Ollert.new
  Mongoid.purge!
end

And add the @javascript tag to our Cucumber feature files. I’ve written about using tags in the past, and they are incredibly useful. For example:

DoStuff.feature
1
2
3
4
5
6
7
8
9
10
11
12
Feature: Do Stuff

Scenario: Nothing happens with Javascript disbaled
  Given I visit the home page
  When I click "Where are they?!"
  Then I should not see "I'm Batman."

@javascript
Scenario: Correct text is displayed with Javascript enabled
  Given I visit the home page
  When I click "Where are they?!"
  Then I should see "I'm Batman."

Assuming there is some Javascript activated by clicking “Where are they?!” that displays the text “I’m Batman.”, both of the above scenarios will pass. This is because none of the Javascript will run in the first scenario, so the text will not be displayed. In the second scenario, Capybara knows to use the webdriver we set up previously when it sees the @javascript tag.

Poltergeist

Poltergeist is the first “headless” web driver I tried. Usage is mostly identical to usage for Selenium, so I’ll focus on installation here.

Poltergeist uses PhantomJS, so we need to start by downloading the binary (32-bit or 64-bit) and putting it in our path. On my Linux machine, I extracted the contents of the download and copied bin/phantomjs to my /usr/local/bin directory, which I already have in my path. You can also copy it directly to /usr/bin if you like.

On the ruby side, we do that same old song and dance: either do gem install poltergeist or add poltergeist to your Gemfile and run bundle install. Edit your env.rb:

env.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ENV['RACK_ENV'] = 'test'

require_relative '../../../web'

require 'capybara/poltergeist'
require 'capybara/cucumber'
require 'rspec'

Capybara.app = Ollert
Capybara.default_wait_time = 10

# use the following web driver to run tests
Capybara.javascript_driver = :poltergeist

World do
  Ollert.new
  Mongoid.purge!
end

Now your Javascript tests should be running using the Poltergeist webdriver. Since Poltergeist is truly headless, your tests will run much faster than they did while using Selenium, but you won’t be able to see what’s going on while your tests run. There are some slight syntactic differences between the way Poltergeist and Selenium handles separate windows, but other than that they are extremely similar.

Webkit

Capybara-webkit is where I eventually landed for running my own tests, after having issues accessing other windows with Poltergeist. Capybara-webkit is also headless and relies on QtWebKit to render pages. So, for starters, you’re going to have to install qtwebkit. This has a varied degree of difficulty depending on which operating system you’re using, but I didn’t have too many problems in Ubuntu once I figured out which library I needed. For help, check the guide. On my machine:

1
$ sudo apt-get install libqtwebkit-dev

Once more: either do gem install capybara-webkit or add capybara-webkit to your Gemfile and run bundle install. Edit your env.rb:

env.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ENV['RACK_ENV'] = 'test'

require_relative '../../../web'

require 'capybara/cucumber'
require 'capybara/webkit'
require 'rspec'

Capybara.app = Ollert
Capybara.default_wait_time = 10

# use the following web driver to run tests
Capybara.javascript_driver = :webkit

World do
  Ollert.new
  Mongoid.purge!
end

Again, you won’t be able to see your tests run, but they should be pretty snappy. I was able to use capybara-webkit to tackle some window issues I was having, but (as of this writing) capybara-webkit has not caught up with more modern capybara window-switching syntax. Other than that, the syntax is identical to the other drivers I’ve discussed for common cases. If you’re running capybara-webkit on a CI server, see this post about using Xvfb.

Use NVM to Manage Your NodeJS Install

| Comments

I’ve been trying to get into NodeJS, and so my immediate thought is that I want to be able to install different versions for different projects, a la rvm for ruby. Fortunately, this already exists.

NVM gives me the equivalent functionality of rvm but for NodeJS, making all my dreams come true. Here’s how I installed it on my Linux boxes:

1
$ curl https://raw.githubusercontent.com/creationix/nvm/v0.12.2/install.sh | bash

Note: This URL will get you nvm version 0.12.2. This link may not be valid in the future, where you come from. Check out the github repo for any newer versions. If you’re brave, trusting, or just really naïve, you can even change v0.12.2 above to master to get the bleeding edge install.

The above line of code will download the files, install nvm in your home directory, and update your profile to include nvm’s current Node version in your path. NVM autocomplete isn’t in place by default, but we can enable it by adding the following to the end of our .bash_profile:

1
[[ -r $NVM_DIR/bash_completion ]] && . $NVM_DIR/bash_completion

Now it’s time to actually install us some Node! You can use nvm ls-remote to list the versions of Node currently available for download. At the time of this writing, the most recent version is v0.11.13. Installing is easy (and quick):

1
2
3
$ nvm install 0.11.13
...
$ nvm use 0.11.13

Since this is the only Node on my system, I’d like to set it as the default.

1
$ nvm alias default 0.11.13

What if I’m cding out of control and I don’t know what Node version I need in my current directory? Create a file called .nvmrc in the directory containing the version number you want nvm to use and then type nvm use ENTER; Now you’re using the version of Node you meant to. This also prevents people from using the wrong versions of Node to try to run your code, which would of course be a catastrophe.

PLUS nvm installs the right version of npm whenever you install Node, so there’s no need to worry about dealing with your base npm not working with different versions of Node.

Thank FOSS for nvm. Maybe one day soon I’ll do useful things in Node an tell you about them.

Ollert - Reveal the Data Behind Your Trello Boards

| Comments

Introducing Ollert, a tool to show you what your Trello boards can’t tell you on their own.

Trello does a great job of telling you about the here and now of your Trello boards; who’s working on what task and what is the current state of a task. But what if I want to know about the state of the whole project? How many work items have we finished in the past sprint, iteration, or duration of the project? He who does not understand the past is destined to repeat his mistakes ad nauseum.

Ollert is a tool with these ideas in mind. Authorize Ollert access to your Trello account, select the board you would like to learn more about, and see all the battle scars your board has experienced throughout the duration of the project.

Learn about your velocity through a Cumulative Flow Diagram. View your work in progress with a WIP chart. See which labels your team uses most frequently with our label chart. See information about your cards and lists from a historical perspective.

Create an account with Ollert to show your support. Users who sign up for an Ollert account will have their Trello authorization saved, saving the hassle of authorizing with Trello every time the user visits the site. An Ollert account is perfect for any user who views board progress frequently, or for creating a continuous display on an office monitor.

Since Ollert’s initial release several months ago, Ollert now boasts over 4000% faster load times, increased data security using SSL and encrypted cookies, and a more consistent Trello connection experience in all modern web browsers.

Discover Ollert now at ollertapp.com.

Testing Automated Emails With Email-spec in Cucumber

| Comments

Now that I send emails using Pony, I want to be able to verify that the emails are being generated correctly. I also don’t want to send real emails and have my tests check an inbox somewhere. I found a couple of solutions to do this, including pony-test and email-spec. Although pony-test fits my needs perfectly, the last commit was December 27, 2011 (2.5 years ago at the time of this post), and thus was using an outdated version of capybara which I was unwilling to use. Fortunately, pony-spec is mostly just a fork of email-spec with all the non-Pony components ripped out.

I’m going to be using Cucumber to test my emails, but email-spec also boasts compatibility with rspec and Turnip. To get started:

1
$ gem install email-spec

The developers of email-spec were kind enough to give us some free step definitions. If I was using rails, I could just type rails generate email_spec:steps, but since I’m using Sinatra I opted just to copy-paste the file into my step_definitions/ directory. You can find email_steps.rb on Github.

In my last post about small horses and emails, I used the following code to send a confirmation email on signup:

web.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ...
post '/signup' do
  user = User.create! params

  url = "#{request.base_url}/account/reset/#{user.generate_verification_hash}"
  Pony.mail(
    to: user.email,
    from: "MyApp Help Desk <noreply@myapp.com>",
    subject: "MyApp Account Verification",
    body: "A request has been made to verify your MyApp account (https://myapp.com)." +
          "If you made this request, go to " + url + ". If you did not make this request, ignore this email.",
    html_body: haml(
      :verify_account_email,
      layout: false,
      locals: {
        email: user.email,
        date: DateTime.now.strftime("%H:%M:%S%P %B %d, %Y"),
        ip: request.ip,
        url: url
      }
    )
  )
end
# ...
/views/verify_account_email.haml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
%p
  Hello!
%p
  An account verification has been requested for your new <a href="https://myapp.com">MyApp</a> account.

%ul
  %li
    Username: #{locals[:email]}
  %li
    Time: #{locals[:date]}
  %li
    IP address: #{locals[:ip]}

%p
  If you made this request, click the link below or copy-paste the following URL into your browser to verify your account:

%p
  %a{href: "#{locals[:url]}", alt: "Verify", title: "Click to verify account"}
    #{locals[:url]}

%p
  If you did not request this new account, please ignore this email.

%p
  Sincerely,
  %br
  Team MyApp

%p
  This email account is not monitored and will not receive replies. For more information, contact <a href="mailto:connect@myapp.com">connect@myapp.com</a>.

Given the pre-defined steps from email-spec, testing that this email gets sent is a breeze. Adding a scenario to my feature file:

features/SignupConfirmation.feature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Feature: Signup Confirmation
  As a new user
  When I sign up
  I should receive a confirmation email

Background:
  Given a clear email queue
  When I go to the signup page
  And I fill in "email" with "prez@whitehouse.gov"
  And I fill in "password" with "bunnies"
  And I press "Sign Up"
  Then "prez@whitehouse.gov" should receive an email

Scenario: Receives email with correct contents
  When "prez@whitehouse.gov" opens the email
  Then they should see the email delivered from "MyApp Help Desk <noreply@myapp.com>"
  And they should see "MyApp Account Verification" in the email subject
  And they should see "Username: prez@whitehouse.gov" in the email body
  And they should see "An account verification has been requested"

That’s it. Now we know that an email like the one above will be sent during signup. What we can’t test here is that our SMTP server (or equivalent) is working, so in reality I’m only testing that the email will attempt to send that looks like the one I test against.

Sending Emails With Pony and Sendgrid

| Comments

It’s incredible how easy it is to send emails through a web application, there’s no wonder we get so much spam. Assuming we have a ruby app using Sinatra, Pony is one of the easiest ways to get started with your own spam empire.

Installation is, as always, trivial with ruby. Install the gem with gem install pony or add gem pony to your Gemfile and run bundle install.

I like to configure Pony in my application’s configure block. I could also add it to my config.ru, but I like to keep that file as tiny as posible to avoid having configuration code all over the place.

web.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ...

require 'pony'
require 'sinatra/base'

class Application < Sinatra::Base
  configure do
    # ...

    Pony.options = {
      :via => :smtp,
      :via_options => {
        :address => 'smtp.sendgrid.net',
        :port => '587',
        :domain => 'myapp.com',
        :user_name => ENV['SENDGRID_USERNAME'],
        :password => ENV['SENDGRID_PASSWORD'],
        :authentication => :plain,
        :enable_starttls_auto => true
      }
    }
  end

  # ...
end

This block tells Pony to use the SendGrid server to send mail, use the “myapp.com” HELO domain, and dig up the username and password fields from my environment.

If you’re using Heroku to host your application, you can sign up for a SendGrid account through your Heroku app, which gives you instant access to your SendGrid account. The username and password field you need to fill in your environment are automatically populated in your Heroku config, which you can view by running heroku config for your application. The free account gets you up to 200 emails a day.

Since I might have multiple developers working in my source code and testing the email-sending functionality, I have all the developers sign up for their own free SendGrid account. This should help to alleviate some of the email volume from any particular account while developing. After signing up, it took my account nearly 4 hours to be “provisioned” (see: approved) by the SendGrid team. Once you’re approved you can start sending emails using your developer account credentials. I stick my username/password in my local .env file (another reason to make sure you’re not storing your environment on your server or in your git repo).

So let’s actually send an email. Let’s create a route that sends an email to verify a new user account; I’ll take some liberties by saying we have a User model defined already that generates a signup verification hash. I can tell pony to send a plaintext body through the body option and an HTML body through the html_body option.

web.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ...
post '/signup' do
  user = User.create! params

  url = "#{request.base_url}/account/reset/#{user.generate_verification_hash}"
  Pony.mail(
    to: user.email,
    from: "MyApp Help Desk <noreply@myapp.com>",
    subject: "MyApp Account Verification",
    body: "A request has been made to verify your MyApp account (https://myapp.com)." +
          "If you made this request, go to " + url + ". If you did not make this request, ignore this email.",
    html_body: haml(
      :verify_account_email,
      layout: false,
      locals: {
        email: user.email,
        date: DateTime.now.strftime("%H:%M:%S%P %B %d, %Y"),
        ip: request.ip,
        url: url
      }
    )
  )
end
views/verify_account_email.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
%p
  Hello!
%p
  An account verification has been requested for your new <a href="https://myapp.com">MyApp</a> account.

%ul
  %li
    Username: #{locals[:email]}
  %li
    Time: #{locals[:date]}
  %li
    IP address: #{locals[:ip]}

%p
  If you made this request, click the link below or copy-paste the following URL into your browser to verify your account:

%p
  %a{href: "#{locals[:url]}", alt: "Verify", title: "Click to verify account"}
    #{locals[:url]}

%p
  If you did not request this new account, please ignore this email.

%p
  Sincerely,
  %br
  Team MyApp

%p
  This email account is not monitored and will not receive replies. For more information, contact <a href="mailto:connect@myapp.com">connect@myapp.com</a>.

When you have a user hit this route, an email will be sent to the user with the given subject, to, from, and body fields using the configuration parameters given in the previous configure block. Fast, easy, and, best of all, no sendmail configuration.