We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

Rails: FileColumn schneller testen durch Mocks

March 11, 2007 by alex

FileColumn
class User
  file_column :photo, :magick => { :versions => {
	:normal => "238x",
	:tiny => "26x35!",
	:small => {:crop => "1:1", :size => "73x73!", :name => "small"},
  }}
end

Dieser Code erzeugt neben der Originalversion 3 Kopien: Eine 238 Pixel breite und entsprechend des Bildformats hohe, eine gestauchte, genau 23 x 35 Pixel grosse und eine genau 73 x 73 Pixel grosse, beschnittene Version.

So weit alles schoen. Das einzige Problem war, dass unsere Unit-Tests durch FileColumn ganz schoen langsam wurden. Bei jedem Testdurchlauf, der irgendwas mit Bilder zu tun hatte, wurden jedes mal mehrere skalierte Versionen erzeugt, was natuerlich Rechenzeit kostete. Die Loesung haben wir letzte Woche dann endlich mal zusammengehackt, und sie sieht so aus: Ein Modul, was von FileColumn installierte Callbacks ausser Gefecht setzt, wird in ein Mock des Models eingebunden, aber der Reihe nach. Hier das Modul, gelegen in test/file_column_mock.rb:

module FileColumnMock

  def mock_file_column(attr)
    define_method "#{attr}=" do |value|
      if value.is_a?(File)
        write_attribute attr, value.path
      else
        write_attribute attr, value
      end
    end

    define_method "#{attr}_after_assign" do
    end

    define_method "#{attr}_after_save" do
    end

    define_method "#{attr}_state" do
      DummyState.new self.send(:read_attribute, attr)
    end

    define_method "#{attr}_after_destroy" do
    end
  end

  class DummyState

    def initialize(file_name)
      @file_name = file_name
    end

    def has_magick_errors?
      false
    end

    def absolute_path(*args)
      @file_name
    end
    def relative_path(subdir = nil)
      @file_name
    end

    def temp_path
      @file_name
    end

    def assign_temp(temp_path)
      nil
    end
  end
end

In unserem Beispiel wollen wir die Klasse User von ihrer FIleColumn-Last befreien. Dazu legen wir in test/mocks/test eine Datei user.rb an und fuellen sie mit folgendem Inhalt:

require 'models/user'
require File.dirname(__FILE__) + '/../../file_column_mock'

class User
  extend FileColumnMock
  mock_file_column :photo
end

Der Effekt des ganzen ist, dass die Original User-Klasse aus app/models nochmals geoeffnet und mehrere Methoden, die normalerweise automatisch beim Setzen des photo-Attributs loslaufen, umd die skalierten Versionen zu erzeugen, durch Ueberschreiben ausser Gefecht gesetzt werden.

Das war’s. Und schon fliegen die Tests wieder mit voller Geschwindigkeit.