Frage

Zunächst einmal weiß ich, dass es eine schlechte Praxis ist, im Unit-Test mehrere Behauptungen zu haben.

Aber manchmal muss man eine atomare Transaktion testen.Nehmen wir als vereinfachtes Beispiel eine Bankanwendung mit der Account-Klasse:

class Account 
  attr_accessor :balance

  def transfer(to_account, amount)
    self.balance -= amount
    to_account.balance += amount
    Audit.create(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
  end

end

In dieser Situation möchte ich drei Dinge gemeinsam überprüfen:

  1. Der Saldo des Quellkontos verringerte sich um amount
  2. Der Saldo des Zielkontos wurde um erhöht amount
  3. Audit-Datensatz wird eingefügt

Wie kann man das am besten testen? @account.transfer Methode?

War es hilfreich?

Lösung

In dieser Situation möchte ich drei Dinge gemeinsam überprüfen:

Ich würde behaupten, dass Sie eigentlich das Verhalten dieser Dinge unter bestimmten Bedingungen beschreiben und so sicherstellen möchten, dass das Verhalten Ihren Spezifikationen entspricht.Das könnte bedeuten, dass Dinge gemeinsam passieren;Oder es könnte bedeuten, dass einige Dinge nur unter bestimmten Bedingungen passieren und unter anderen nicht, oder dass eine Ausnahme dazu führt, dass alles auf seinen ursprünglichen Zustand zurückgesetzt wird.

Es ist keine Zauberei, alle Ihre Behauptungen in einem Test zu haben, außer um die Dinge schneller zu machen.Sofern Sie nicht mit erheblichen Leistungseinbußen konfrontiert werden (was bei Full-Stack-Tests häufig der Fall ist), ist es viel besser, eine Behauptung pro Test zu verwenden.

RSpec macht es einfach, die Testaufbauphase zu extrahieren, sodass sie für jedes Beispiel wiederholt wird:

class Account 
  attr_accessor :balance

  def transfer(to_account, amount)
    self.debit!(amount)
    to_account.credit!(amount)
    Audit.create!(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
  rescue SomethingBadError
    # undo all of our hard work
  end

end

describe Account do
  context "when a transfer is made to another account" do
    let(:other_account} { other_account }
    context "and the subject account has sufficient funds" do
      subject { account_with_beaucoup_bucks }
      it "debits the subject account"
      it "credits the other account"
      it "creates an Audit entry"
    end
    context "and the subject account is overdrawn" do
      subject { overdrawn_account }
      it "does not debit the subject account"
      it "does not credit the other account"
      it "creates an Audit entry" # to show the attempted transfer failed
    end
  end
end

Wenn alle drei Tests im „glücklichen Pfad“ bestanden wurden, dann passierten sie alle „zusammen“, da der anfängliche Systemzustand in jedem Fall derselbe war.

Aber Sie müssen auch sicherstellen, dass die Dinge nicht passieren, wenn etwas schief geht und das System in seinen ursprünglichen Zustand zurückkehrt.Wenn Sie mehrere Behauptungen haben, können Sie leicht erkennen, ob dies wie erwartet funktioniert, und genau dann, wenn Tests fehlschlagen Wie Sie versagten.

Andere Tipps

Mehrere Behauptungen pro Test sind nicht immer eine schlechte Praxis.Wenn die mehreren Asserts dasselbe Verhalten bestätigen, liegt kein Problem vor.Das Problem besteht, wenn versucht wird, mehr als ein Verhalten im selben Test zu überprüfen.Natürlich gibt es einige Risiken bei mehreren Behauptungen pro Test.Einer davon besteht darin, dass Sie möglicherweise versehentlich Werte aus einem vorherigen Testsatz hinterlassen, wodurch ein vorheriger Test auf seltsame Weise ungültig wird.Wenn außerdem eine Behauptung falsch ist, werden alle anderen verbleibenden Behauptungen nicht ausgeführt, was dazu führen kann, dass es schwierig ist, zu verstehen, was vor sich geht.Aber seien Sie vernünftig, Sie können mehrere Asserts haben, die dasselbe Verhalten bestätigen, vorzugsweise kurze und ohne zusätzliche Einrichtung.

In dem einfachen Fall, den Sie angeführt haben, würde ich mehrere Asserts verwenden, weil es so einfach ist.Aber es kann natürlich noch viel komplizierter werden, wie zum Beispiel ein negativer Kontostand, verschiedene Arten von Konten und so weiter.Dann wäre es besser, verschiedene Tests mit einem (vorzugsweise) Assert zu verwenden.Ich würde es so organisieren:

  • 1 um das Verhalten des Girokontos zu testen (einfachster Fall);
  • 1 auf jeden verschiedenen Pfad die Methode haben (Ausnahmen, negatives Gleichgewicht usw.);
  • 1, um Audit in jeder dieser Möglichkeiten zu testen;

  • 1 um das Verhalten des aktuellen to_account zu testen (einfachster Fall);

  • 1 zu jedem anderen Pfad, den die Methode haben kann.(Ausnahmen, negatives Gleichgewicht usw.);
  • 1, um Audit in jeder dieser Möglichkeiten zu testen;

Da der Audit-Test ziemlich einfach ist und keine zusätzliche Einrichtung erfordert, können Sie ihn auch zusammen mit Account und to_account testen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top