Testen: Arrange, Act, Assert

Die Notwendigkeit von automatisierten Tests ist unumstritten.
Sie

  1. sichern Refactoring Schritte ab
  2. helfen, Schwächen in der Implementierung aufzudecken
  3. dokumentieren die implementierte Logik
  4. erlauben das Implementieren von Modulen, auch wenn dafür notwendige Module noch fehlen (Mocking)

Grundsätzlich bestehen Tests aus 3 Schritten: Arrange, Act und Assert. Das heißt die Testumgebung herstellen (Arrange), die zu testende Logik aufrufen (Act) und die Erwartung prüfen (Assert).
Als einfaches Beispiel soll ein Bankkonto dienen, auf das Beträge eingezahlt werden können. Vorab schon einmal die Implementierung:

class BankAccount
  attr_reader :total

  def initialize(amount)
    @total = amount
  end

  def <<(amount)
    @total += amount
  end
end

Der Test ist sehr einfach, soll an dieser Stelle aber explizit und ausführlich in die 3 Schritte unterteilt werden.

Arrange

Um den Test ausführen zu können, muss die entsprechende Umgebung (notwendige Objekte etc.) hergestellt werden. Dieser Schritt ist sehr entscheidend für die Qualität des Tests. Schlechte Tests erstellen unnötig viele Objekte oder erstellen sogar Objekte, die die Aussage des Tests komplett ad absurdum führen. Auch unnötige Zugriffe auf langsame oder externe Systeme (Datenbank, Dateisystem, Web) sollten vermieden werden.
Um die Methode « zu testen, wird mindestens das zu testende Objekt (subject) benötigt:

require 'spec_helper'

RSpec.describe BankAccount do
  # *** Arrange Beginn ***
  subject { BankAccount.new 100 }
  # *** Arrange Ende ***

  describe '#<<' do
    it 'adds the passed amount to total' do
    end
  end
end

Act

Das Ausführen der zu testenden Logik ist oft der einfachste Schritt:

require 'spec_helper'

RSpec.describe BankAccount do
  subject { BankAccount.new 100 }

  describe '#<<' do
    it 'adds the passed amount to total' do
      # *** Act Beginn ***
      subject << 1
      # *** Act Ende ***
    end
  end
end

Assert

Um zu prüfen, ob das Resultat der Erwartungshaltung entspricht, ist es wichtig, dass sie kurz und klar formuliert wird. Nur so kann sie auch eine gute Dokumentation der Logik sein. Schwer verständliche Erwartungshaltungen erschweren auch das Verständnis, was die Implementierung machen soll:

require 'spec_helper'

RSpec.describe BankAccount do
  subject { BankAccount.new 100 }

  describe '#<<' do
    it 'adds the passed amount to total' do
      subject << 1
      # *** Assert Begin ***
      expect(subject.total).to eq(101)
      # *** Assert Ende ***
    end
  end
end