Testing shared traits or modules without repeating yourself

When two classes implement the same behavior (methods, callbacks, etc.), you should extract that behavior into a trait or module. This card describes how to test that extracted behavior without repeating yourself.

Note that the examples below use Modularity traits to extract shared behavior. This is simply because we like to do it that way at makandra. The same techniques apply for modules and overriding self.included.

Example
---...

Pitfall: ActiveRecord callbacks: Method call with multiple conditions

In the following example the method update_offices_people_count won't be called when office_id changes, because it gets overwritten by the second line:

after_save :update_offices_people_count, :if => :office_id_changed? # is overwritten …
after_save :update_offices_people_count, :if => :trashed_changed? # … by this line

Instead write:

after_save :update_offices_people_count, :if => :office_people_count_needs_update?

private

def office_people_count_needs_update?
  office_id_changed? || trashed_changed?
end

Or...

Javascript: Comparing two arrays for equality

Don't Google this, you will lose all will to live. Instead use Object#isEqual() from Lodash or Underscore.js:

_.isEqual([1, 2], [2, 3]) // => false
_.isEqual([1, 2], [1, 2]) // => true

If your project already uses Unpoly you may also use up.util.isEqual() in the same way:

up.util.isEqual([1, 2], [2, 3]) // => false
up.util.isEqual([1, 2], [1, 2]) // => true

To compare two arrays for equality in a Jasmine spec assertion, see [Jasmine: Test...

Capturing signatures on a touch device

If you need to capture signatures on an IPad or similar device, you can use Thomas J Bradley's excellent Signature Pad plugin for jQuery.

To implement, just follow the steps on the Github page.

The form

If you have a model Signature with name: string, signature: text, you can use it with regular rails form like this:

- form_for @signature, :html => { :class => 'signature_form' } do |form|
  %dl
    %dt
      = form...

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
--------------------...

How to: Specify size of Selenium browser window

Applications often show or hide elements based on viewport dimensions, or may have components that behave differently (like mobile vs desktop navigation menus).
Since you want your integration tests to behave consistently, you want to set a specific size for your tests' browser windows.

Using WebDriver options / Chrome device metrics

For Google Chrome, the preferred way is setting "device metrics". This allows you to configure dimensions larger than your display and enable/disable touch behavior.

Simply use register_driver to set up...

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...

Authorize allowed values with assignable_values

All our projects have enum-like requirements like this:

  • An attribute value must be included in a given set of values.
  • The list of allowed values must be retrievable in order to render <select> boxes.
  • Each value has a humanized label.
  • Sometimes there is a default value.

Most of the time, this requirement is also needed:

  • The list of assignable values depends on the user who is currently signed in.

In our past projects there are many different solutions for these related requirements, e.g. ChoiceTrait, methods like `available_...

VirtualBox host IP address and hostname

When you are using NAT in your virtual machine (which you should), the host's IP address is:

10.0.2.2

You'll need it to access shared folders or your host's web server when testing pages in IE.

Fun fact: You could also use vbox.srv -- that's the corresponding hostname.

MySQL: For each group, retrieve a comma-separated list of values in a given column

The technique described in this card has an important caveat: The result of GROUP_CONCAT is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. This will cause horrible, data-destroying bugs in production. For this reason you should probably not use GROUP_CONCAT ever. At least you must set the value of group_concat_max_len to an insanely high value on every database server your application runs on.


Lik...

Linux: Create file of a given size

Sometimes you need a file of some size (possibly for testing purposes). On Linux, you can use dd to create one.

Let's say you want a 23 MB file called test.file. You would then run this:

dd if=/dev/zero of=test.file bs=1048576 count=23

The block size (bs) is set to 1 MB (1024^2 bytes) here, writing 23 such chunks makes the file 23 MB big.\
Adjust to your needs.

This linux command might also come in handy in a Ruby program. It could be used like:

mb = 23
mb_string, _error_str, _status = Open3.capture3('dd if=/dev/zero...

RubyMine crashes Ubuntu 11.04 window decorator on exit

My RubyMine (and it seems like many other Java GUI applications) crashes the Compiz window decorator almost every time on exit. This also seems to happen for the Unity decorator.

Update: The commited fix from below seems to have made it into the stable Ubuntu repository.

Easy mode

You can restore window decorations by executing this command:

gtk-window-decorator --replace &

This is only a temporary fix.

Hard mode

Also, there is a committed fix that is n...

How to test resource_controller hooks

When using the resource_controller gem you often hook onto events like this:
update.before do
do_something
end

For testing such things in your controller you should -- as always -- not trigger something that eventually calls the thing you want.\
Instead, in your specs, have resource_controller run those hooks like it does itself. Like that:

describe 'before update' do
 ...