Functional Tests einer Rails 2.3.x Anwendung mit Paperclip und Clearance 0.8.8

Endlich habe ich mal Zeit gefunden, für eine etwas betagtere Rails-Anwendung (Rails 2.3.14) die Tests zu schreiben. Das Scaffolding erstellt zwar ein Grundgerüst, das aber bei fortgeschrittener Entwicklung immer angepasst werden muss. Die Unit-Tests waren kein großes Problem. Bei den Functional-Tests wurde es schon komplizierter. In der Anwendung kommt Paperclip als Gem zum Einsatz, um verschiedene Models mit einem Bild zu erweitern. Zur Authentifizierung wird Clearance als Plugin verwendet und liegt in Version 0.8.8 vor. Diese Version ist schon etwas älter und zum Testen wird Cucumber empfohlen. Paperclip hat sogenannte Matchers für RSpec. Ich wollte aber keines der beiden benutzen und stattdessen mit Rails-eigenen Mitteln (test/unit) Tests schreiben und die durch das Scaffolding automatisch erstellten Tests erweitern.

Mein erstes Problem waren die Fixtures. Da war es recht schwierig ein Bild festzulegen und somit automatisch laden zu lassen. FactoryGirl hat ein ziemlich gutes Konzept, um Fixtures zu erstellen und wird auch ziemlich häufig verwendet. Damit konnte ich dann ganz einfach Fixtures, sogenannte Factories, mit einer Bilddatei erstellen, die dann von meinem Model und damit von Paperclip anstandslos geschluckt wurden.

# test/factories/factories.rb
Factory.define :picture do |picture|
  picture.data    { File.new(RAILS_ROOT + '/test/factories/bg.jpg') }
  picture.author  { "test author" }
  picture.gallery { Factory(:gallery) }
end

Um FactoryGirl anstatt der Standard-Fixtures zu verwenden, habe ich einfach das factory_girl gem zu meiner config/environments/test.rb hinzugefügt,

# config/environments/test.rb
config.gem 'factory_girl'

das Laden der Fixtures in der test_helper.rb auskommentiert,

# fixtures :all

den fixtures Ordner gelöscht und eine factories.rb angelegt. In dieser kann ich nun fleißig Factories mit allerlei Funktionen anlegen. Für weiteres einfach in die FactoryGirl Doku gucken.

Als das erledigt war, konnte ich anfangen die Tests zu schreiben. Da ich Clearance als Authentifizierung verwende, musste ich erst einmal herausfinden, wie ich das Ein- und Ausloggen mit den Tests berwerkstelligen kann. Das Gem ist schon recht alt und deswegen war es etwas schwierig darüber etwas Doku zu finden. Ich habe es wie folgt gelöst.

Meine test_helper.rb bindet die Helpers von Clearance ein.

# test/test_helper.rb
require File.join(File.dirname(__FILE__), '..', 'vendor', 'plugins', 'clearance', 'shoulda_macros', 'clearance')

Jetzt kann ich alle meine Tests mit einer setup und teardown methode ausstatten, die die Clearance Methoden signin und signout nutzen können, um das Ein- und Ausloggen zu bewerkstelligen.

# test/functional/pictures_controller_test.rb
def setup
  sign_in
  @picture = Factory.create(:picture)
end

def teardown
  sign_out
  @picture = nil
end

Dabei wird außerdem eine Instanzvariable angelegt, die ich in den Tests anstatt der Fixtures verwende. Andere Teile des Tests, die noch die alten Fixtures verwenden, musste ich noch austauschen.

# test/functional/pictures_controller_test.rb
test "should create picture" do
  assert_difference('Picture.count') do
    post :create, :picture => Factory.attributes_for(:picture)
  end

  assert_redirected_to picture_path(assigns(:picture))
end

test "should show picture" do
  get :show, :id => @picture.id
  assert_response :success
end

test "should get edit" do
  get :edit, :id => @picture.id
  assert_response :success
end

test "should update picture" do
  put :update, :id => @picture.id, :picture => Factory.attributes_for(:picture)
  assert_redirected_to picture_path(assigns(:picture))
end

test "should destroy picture" do
  assert_difference('Picture.count', -1) do
    delete :destroy, :id => @picture.id
  end

  assert_redirected_to pictures_path
end

Die kompletten functional-Tests gibt es hier. Damit laufen alle Tests für die Anwendung erfolgreich. Ich bin mir garnicht sicher, ob das der beste Weg ist, um die Tests so zu bauen und daher für Verbesserungsvorschläge dankbar.