Jasmine: Mocking API requests in an Angular service spec

Situation: You want to write a spec for a function inside an Angular service. This function at some point makes an API request and acts upon response. Notably, your Angular app employs uiRouter, although it is not used nor actually required for this test.

Working test setup

# Capitalized expressions are intended to be replaced with YOUR values

describe 'SERVICE', ->

  beforeEach ->
    module 'MODULE'

    module ($urlRouterProvider) ->
      # Prevent uiRouter's initialization, i.e. do not sync the current URL
      # with its r...

Rails: How to check if a certain validation failed

If validations failed for a record, and you want to find out if a specific validation failed, you can leverage ActiveModel's error objects.
You rarely need this in application code (you usually just want to print error messages), but it can be useful when writing tests.

As an example, consider the following model which uses two validations on the email attribute.

class User < ApplicationRecord
  validates :email, presence: true, uniqueness: true
end

Accessing errors

Let's assume we have a blank user:

user = Us...

Upgrading Ruby from 1.8.7 to 2.3.5

Suggested Workflow

Set the ruby version in .ruby-version to 2.3.5, then perform these steps one by one, fixing errors as they occur:

  1. Update gems as listed below, and bundle
  2. Boot a Rails console - see below for a list of changes you will probably need
  3. Run Specs with --backtrace option
  4. Run Cucumber features (with Geordi's --debug option)
  5. When all tests are green, look through your Gemfile and remove as many version constraints as possible.
  6. Boot the application in different environements to spot further issues, e...

RSpec & Devise: How to sign in users in request specs

You know that Devise offers RSpec test helpers for controller specs. However, in request specs, they will not work.

Here is a solution for request specs, adapted from the Devise wiki. We will simply use Warden's test helpers -- you probably already load them for your Cucumber tests.

First, we define sign_in and sign_out methods. These will behave just like ...

How to bind an event listener only once with Unpoly

You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }:

up.on('up:fragment:inserted', { once: true }, function () { ... })

In Unpoly 1 you can immediately unregister the listener with up.off:

up.on('up:fragment:inserted', function fragmentInsertedCallback() {
  up.off('up:fragment:inserted', fragmentInsertedCallback)
  // ... code for the callback function, which should run only once
})

Exam...

Geordi 1.5.1 released

  • Improve geordi cucumber: Only attempt @solo run when the specified files contain the @solo tag, skip @solo run if any filename is passed with a line number (e.g. features/example.feature:3)
  • Improve geordi deploy: Find stages by their prefix (e.g. s -> staging, m -> makandra), bundle if needed, check the selected stage exists
  • Improve geordi server: Takes port as argument (e.g. geordi ser 3001), option --public (-P) starts the server with -b 0.0.0.0 to make it accessible from other machines in the local network, e.g. ...

Delete all MySQL records while keeping the database schema

You will occasionally need to clean out your database while keeping the schema intact, e.g. when someone inserted data in a migration or when you had to kill -9 a frozen test process.

Old Capybara versions already have the Database Cleaner gem as dependency. Otherwise add database_cleaner to your *Gemfile`. This lets you say this from the Rails console:

DatabaseCleaner.strategy = :truncation
DatabaseCleaner.cl...

Hide your Selenium browser window with a VNC server

This is now part of geordi. Please don't follow the instructions below, if you use geordi.

Inspired by the recent headless Selenium note, I found yet another solution for the problem to hide your selenium tests away.

This has the advantages
^

  • not to require a gem (so you do not force this on others)
  • to allow you to take a look at the running webdriver if necessary

Simply make a script th...

An incomplete guide to migrate a Rails application from paperclip to carrierwave

In this example we assume that not only the storage gem changes but also the file structure on disc.

A general approach

Part A: Create a commit which includes a script that allows you to copy the existing file to the new file structure.

Part B: Create a commit which removes all paperclip logic and replace it with the same code you used in the first commit

Part A

Here are some implementation details you might want to reuse:

  • Use the existing models to read the files from
  • Use your own carrierwave models to write t...

Run Chrome in a specific resolution or user agent with Selenium

When you want to test how an web-application reacts in a specific resolution, you can set up a specific Selenium driver for some tests:

 Before('@chrome320x480') do
     Capybara.current_driver = :chrome320x480
 end

 After('@chrome320x480') do
    Capybara.use_default_driver 
 end

You can use either chromium or chrome beta (as of 2012.05 the Version "19.0.1084.41 beta" works), or any other member of the family. It only needs to supports the "--window-size" command-line switch. [See this list](http://peter.sh...

Testing setTimeout and setInterval with Jasmine

Jasmine has a jasmine.clock() helper that you can use to travel through time and trigger setTimeout and setInterval callbacks:

beforeEach(function() {
  timerCallback = jasmine.createSpy("timerCallback");
  jasmine.clock().install();
});

afterEach(function() {
  jasmine.clock().uninstall();
});

it("causes a timeout to be called", function() {
  setTimeout(function() {
    timerCallback();
  }, 100);

  expect(timerCallba...

GitHub Actions: Retrying a failing step

If you have a flaky command you can use the nick-invision/retry to re-try a failing command, optionally with a timeout:

---
...
jobs:
  test:
    ...
    steps:
    - name: Run tests
      uses: nick-invision/retry@v2
      with:
        timeout_seconds: 30
        max_attempts: 3
        command: bundle exec rake spec

Speed up RSpec by deferring garbage collection

Update: This trick probably isn't very useful anymore in Ruby 2.x. The Ruby GC has improved a lot over the years.


Joe Van Dyk discovered that running the Ruby garbage collector only every X seconds can speed up your tests. I found that deferring garbage collection would speed up my RSpec examples by about 15%, but it probably depends on the nature of your tests. I also tried applying it to Cucumber f...

Don't call #node on a Capybara element

Capybara allows you to select DOM elements, e.g. by using field, find_field(...) or field_labeled(...):

role_select = field_labeled('Role')

In the example above, role_select is now a Capybara::Driver::Node. You can call a number of methods on such a node, e. g. in order to click it or to make a selection on its descendants. These methods should be the same regardless of the driver Capybara is using (drivers are e.g. the headless Rack::Test or Selenium).

It i...

Firefox ESR Release Calendar

The linked table shows the support lifecycle for Firefox Extended Support Releases (ESR) which we sometimes need to support for enterprise customers.

The ESR cadence works something like this:

  • Firefox ESR freezes the then-current Firefox version for a year.
  • During this year Mozilla backports security patches to the current ESR, but does not add features.
  • Two subsequent ESR releases overlap for three months. This way enterprises have a quarter to test the new version and migrate their clients.

The new Modularity 2 syntax

We have released Modularity 2. It has many incompatible changes. See below for a script to migrate your applications automatically.

There is no does method anymore

We now use traits with the vanilla include method:

class Article < ActiveRecord::Base
  include DoesTrashable
end

When your trait has parameters, use square brackets:

class Article < ActiveRecord::Base
  include DoesStripFields[:name, :brand]
end

Note how you ...

How to solve Selenium focus issues

Selenium cannot reliably control a browser when its window is not in focus, or when you accidentally interact with the browser frame. This will result in flickering tests, which are "randomly" red and green. In fact, this behavior is not random at all and completely depends on whether or not the browser window had focus at the time.

This card will give you a better understanding of Selenium focus issues, and what you can do to get your test suite stable again.

Preventing accidental interaction with the Selenium window
--------------------...

Using Apache Benchmark (ab) on sites with authentication

Apache HTTP server benchmarking tool (ab) is a nice tool to test performance on sites delivered by HTTP. If the site you're about to test is placed behind a login, follow these steps to successfully use ab on it.

  1. Open the site to test in the browser of your choice. Do not login yet.
  2. Use developer tools to show all cookies used by the site. (Chrome: Ctrl+Shift+i, open the 'Resources' tab and click on the site below 'Cookies' on the left. Firefox: Right-click on the site, open 'We...

RSpec: ". not_to include" behaves like ".to exclude"

RSpec is smart when using the include-matcher in combination with .not_to. One could assume that

.not_to include(3, 4, 5)

evaluates to:

NOT( .to include(3, 4, 5) )

However, it behaves like:

.to (NOT include(3) && NOT include(4) && NOT include(5) )

Warning

Using .not_to in combination with the include-matcher doesn't logically negate the final truth value. It instead negates the individual include-expectations for each argument.

Proof

describe 'RSpec' do
  it "doesn't use logical nega...

Use a special version of Chrome for selenium (and another for your everyday work)

Sometimes you need a special version of chrome because it has some features you need for testing, like in this card. You do not need to use that Version apart from tests, because you can tweek selenium to use a special version that you set in your environment:

# features/support/chrome.rb
require "selenium/webdriver"

Capybara.register_driver :chrome320x480 do |app|
  
  if driver_path = ENV["CHROME_SELENIUM_BIN...

Implementing social media "like" buttons: Everything you never wanted to know

So you client has asked you to implement a row of buttons to like the URL on Facebook, Twitter and Google+. Here are some things you should know about this.

0. Security considerations

Each "like" button is implemented by including a Javascript on your site. This means you are running fucking remote code on your page. You are giving Facebook, Twitter and Google+ full permission to e. g. copy user cookies. Check with your client if she is cool with that. Also note that if you're site is suggesting security by operating under HTTPS ...

How to combine greps on log files opened with tail -f

In order to chain greps on log files that are opened via tail -f test.log you have to use the --line-buffered command line option for grep.

Imagine you have the following content in your log file.

# content for log/test.log
test foo
bar
test foo bar baz
bla

Now if you would like to grep for lines that contain foo but not bar, you can use the following command chain:

$ tail -f log/test.log | grep --line-buffered "foo" | grep -v "bar"

Output:
test foo    

Disabling Spring when debugging

Spring is a Rails application preloader. When debugging e.g. the rails gem, you'll be wondering why your raise, puts or debugger debugging statements have no effect. That's because Spring preloads and caches your application once and all consecutive calls to it will not see any changes in your debugged gem.

Howto

Disable spring with export DISABLE_SPRING=1 in your terminal. That will keep Spring at bay in that terminal session.

In Ruby, [you can only write environment variables that subproc...

Jasmine: Reset the location when testing code that uses pushState / replaceState

When testing code that uses pushState / replaceState, your browser will appear to navigate away from http://localhost:3000/specs (or wherever you run your Jasmine tests). This is inconvenient, since reloading the document will no longer re-run the test suite.

To remedy this, copy the attached file to a place like spec/javascripts/helpers and #= require it from your tests. It will store the current location before every test and reset if afterwards (using location.replaceState).