Die Notwendigkeit von automatisierten Tests ist unumstritten.
Sie
- sichern Refactoring Schritte ab
- helfen, Schwächen in der Implementierung aufzudecken
- dokumentieren die implementierte Logik
- 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