Testing custom validators

When writing custom validators like described in the Rails guide on validations it’s good practice to write tests for them so you are sure they work as intended. Let’s take a look at the custom validator example shown in that Rails guide:

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors.add attribute, (options[:message] || "is not an email")
    end
  end
end

The above validator will validate attributes against a regular expression to see if the value is a valid email address, if it is not a valid email address the error “is not an email” is added to the errors for that attribute.

You can test the validator by working with a test model class inside your test like so:

require "test_helper"

class EmailValidatorTest < ActiveSupport::TestCase
  class TestModel
    include ActiveModel::Model

    attr_accessor :email

    validates :email, email: true
  end

  test "recognizes a valid email address" do
    test_object = TestModel.new(email: "mark@without-brains.net")

    assert test_object.valid?
  end

  test "fails validation for an invalid email address" do
    test_object = TestModel.new(email: "no-email")

    refute test_object.valid?
    assert_equal ["is not an email"], test_object.errors[:email]
  end
end

By defining the TestModel class inside the test it will actually be named EmailValidatorTest::TestModel preventing clashes with other tests.

If you want to try the above code in a Rails project you can save the files as app/validators/email_validator.rb and test/validators/email_validator_test.rb respectively and then run the tests.

Using instance_exec to change block scope

When you create a block in Ruby and execute it later it’s context is tied to where it was defined. Take a look at this very contrived example:

class Dog
  attr_accessor :name, :block

  def initialize(name)
    @name = name
    @block = proc do
      puts "#{self.name} the #{self.class.name}"
    end
  end
end

class Person
  attr_accessor :name, :block

  def initialize(name)
    @name = name
  end
end

@fido = Dog.new('Fido')
@mark = Person.new('Mark')
@mark.block = @fido.block

If you load the code above into irb or pry you can see this in action:

[2] pry(main)> @mark.block.call
Fido the Dog
=> nil

In the block self refers to @fido, the instance of the Dog class in which the block was defined. If you want to execute the block in the context of another object you can use instance_exec like so:

[3] pry(main)> @mark.instance_exec(&@fido.block)
Mark the Person
=> nil

You can find the documentation of instance_exec here.

Using find_each to avoid memory problems

When selecting records with ActiveRecord in Rails all of the selected objects are usually loaded into memory, if you are dealing with a large record set (for example in a background job, rake task or migration) this can lead to memory problems that result in your code crashing.

By using find_each at the end your scope you will only a small set of records into memory at a time:

Note.all.find_each { |note| note.update(body: note.body.reverse) }

For more information see the documentation here.

Using the Rails router elsewhere

When working on Rails projects a commonly used part is the Rails router which is used to direct incoming requests to the appropriate controllers and actions and to generate paths and URLs so you don’t have to hardcode them all over the place.

In systems tests and integration tests the Rails router is also available for you to direct your tests to the appropriate URLs, in your regular tests or the Rails console however this is not the case. You can however get access to it by including a module that Rails can generate for you, if you call Rails.application.routes.url_helpers a module is returned that provides the Rails router’s methods.

Continue reading “Using the Rails router elsewhere”

Testing ActiveSupport concern modules

Using modules to share code between classes in Ruby is a commonly used method to add re-usable behaviour without blocking the inheritance chain, the Rails ActiveSupport::Concern adds some convenience methods to do so. What (at least in my opinion) should not be forgotten is to test the usage of such modules on your classes, this can actually be achieved fairly easily creating a module with test code that you can then again include in your tests.

Take this very contrived example module which adds an instance method to the classes it is included in:

module ReverseName  
  def reversed_name 
    name.reverse    
  end               
end                 

It’s function could be tested with the following test:

test 'reverse name' do                             
  assert_equal 'htimS nhoJ', @name_reversable.reversed_name 
end                                                

This can be poured into a module using ActiveSupport::Concern that can be included in test cases of other classes like so:

module Concerns                                          
  module ReverseNameTest                                 
    include ActiveSupport::Concern                       
                                                         
    included do                                          
      test 'reverse name' do                             
        assert_equal 'htimS nhoJ', @name_reversable.reversed_name 
      end                                                
    end                                                  
  end                                                    
end                                                      

In Rails projects I usually put shared test code inside the directory test/test_helpers, to have these loaded when running tests you will have to add the following line to your test_helper.rb:

Dir[Rails.root.join('test/test_helpers/**/*.rb')].sort
                                                 .each { |file| require file }

To now use this test module on a class that can make use of it we can include the test module in that model’s test like so:

require 'test_helper'                                             
                                                                  
class ContactTest < ActiveSupport::TestCase                       
  include Concerns::ReverseNameTest                               
                                                                  
  setup do                                                        
    @name_reversable = Contact.new(first_name: 'John', last_name: 'Smith') 
  end                                                             
                                                                  
  test 'name' do                                                  
    contact = Contact.new(first_name: 'John', last_name: 'Smith') 
    assert_equal 'John Smith', contact.name                       
  end                                                             
end                                                               

If you want to verify that the test gets executed for your model you can purposely break the test(s) to see them fail like shown below:

Running ActiveJobs inline temporarily

Pushing execution of code that take a long time to complete to ActiveJob can be great to increase the (feeling of) responsiveness for any Rails application. Sometimes it can be desirable to run triggered jobs inline instead though (in certain rake tasks for example), you can do this by overriding the queue adapter with the inline adapter like so:

ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::InlineAdapter.new

If you want to ensure that the adapter is switched back to its original setting after you are done (because more code is executed around the part where you want to run the jobs inline) you can that using ensure like in the below example rake task:

desc 'My rake task'
task my_task: :environment do
  old_queue_adapter = ActiveJob::Base.queue_adapter
  ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::InlineAdapter.new
  # your code here
ensure
  ActiveJob::Base.queue_adapter = old_queue_adapter
end

Note that if you are using different queueing backends for specific jobs (as documented in the Rails guide here) you would have to override each one of those too in the same way (if you would want to run those inline that is).

Rails: ActiveRecord descendants in development mode

Single table inheritance (often referenced to as STI) is a feature offered by Rails’s ActiveRecord (which is documented here) that allows you to subclass models to store data for multiple similar models in one database table. This can be a very useful feature under the right circumstances.

When working with Rails in development mode classes are not preloaded (unlike is done in production mode). If you use the descendants method to get a list of the subtypes of your model (to for example display them or to populate a select box) it is possible that sometimes an empty or partial list is returned rather than a list of all the defined subtypes (unless you have actually used one of them so far).

If you are working in a team this can be an especially devious little problem that can linger for a long time. Different developers may have different sets of data in their development system. Loading a list of STI models will load the subtype classes making them available in the application from that point on. If the database however does not contain records for the subtype’s class and you are not directly referencing it you will not see it. This can then result in one developer experiencing issues while the another does not.

In this article I will demonstrate the issue and offer a solution that you can implement.

Continue reading “Rails: ActiveRecord descendants in development mode”

Prepend to an existing rake task

Rake is a commonly used tool in Ruby projects to provide command line tasks, Rails comes with a number of rake tasks included for example. It is quite well documented how to write rake tasks, a while ago however I needed to prepend something to an existing rake task. One way to achieve this is to write a new rake task that executes the code that you want to execute and then calls the existing rake task. The (perhaps obvious) downside to this method is that you have to execute another rake task than you usually would.

It turns out that there is another way, you can use the enhance method on an existing rake task. In this article I will briefly explore how you can use this method.

Continue reading “Prepend to an existing rake task”

Using bundler inline

A little while ago someone showed me a pretty cool feature of Bundler that I didn’t know about: you can use it inline in a Ruby script without having to create a separate Gemfile. This is very convenient for writing little utility scripts.

To use bundler this way you start with a require bundler/inline followed by a gemfile block that contains the content that you would have put in the Gemfile if you would have had to create one. When you then execute the script with Ruby Bundler will first install the required gems (if needed) before executing your code.

Here’s an example that uses the HTTPClient gem to check your external IP address (using the same endpoint I made for my article on obtaining your external IP address from the command-line):

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'httpclient'
end

client = HTTPClient.new
puts client.get('https://ip.5ec.nl').body

If you execute this script for the first time it will pause a moment to install the HTTPClient gem, the next time you execute it will work without delay.

This and other features of Bundler are well documented on the official Bundler documentation pages, it’s well worth having a look around there.

If you have feedback on this article or have questions based on its contents then feel free to reach out to me on Twitter or through e-mail.

Capybara with Selenium and Vagrant (without Rails)

Once upon a time I wrote a blog post about using Capybara with Selenium and Vagrant (which you can still find on the previous incarnation of my blog here) … things have changed a bit since then however: it got easier to run headless browser tests for example (no longer requiring an Xvfb setup) and nowadays Rails even ships with Capybara by default for its systems tests taking away a lot of the complexity of setting it up for developers using the framework.

Capybara with Selenium is still an amazingly good combination for automated testing of web applications in a real browser, so in this article I will revisit running automated browser tests with it on a Vagrant box with the same perhaps somewhat unimaginative little Sinatara application as I did back in 2012.

Continue reading “Capybara with Selenium and Vagrant (without Rails)”