Shoulda + factory: Kann ich meine Tests schneller machen?
-
13-09-2019 - |
Frage
ich nach einer Möglichkeit, meine Shoulda + factory Tests.
Das Modell Ich versuche zu testen (StudentExam
) hat Assoziationen zu anderen Modellen. Diese assoziierten Objekte müssen vorhanden sein, bevor ich eine StudentExam
erstellen können. Aus diesem Grunde werden sie in setup
erstellt.
Doch eines unserer Modelle (School
) nimmt viel Zeit zu erstellen. Da setup
vor jeder should
Anweisung aufgerufen wird, nimmt der gesamte Testfall Äonen auszuführen -. Eine neue @school
schafft, @student
, @topic
und @exam
für jede Anweisung ausgeführt sollte
ich nach einer Möglichkeit, diese Objekte einmal und nur einmal zu erstellen. Gibt es so etwas wie ein startup
für before_all
Methode, die mir erlauben würde, Datensätze zu erstellen, die für den Rest des Testfall anhalten wird?
Im Grunde suche ich nach etwas genau wie RSpec vor (: all) . Ich bin nicht besorgt über die Frage der Abhängigkeiten, da diese Tests werden nie die teueren Objekte ändern.
Hier ist ein Beispiel Testfall. Wir entschuldigen uns für den langen Code (Ich habe auch erstellt ein Kern ):
# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
should_belong_to :student
should_belong_to :exam
setup do
# These objects need to be created before we can create a StudentExam. Tests will NOT modify these objects.
# @school is a very time-expensive model to create (associations, external API calls, etc).
# We need a way to create the @school *ONCE* -- there's no need to recreate it for every single test.
@school = Factory(:school)
@student = Factory(:student, :school => @school)
@topic = Factory(:topic, :school => @school)
@exam = Factory(:exam, :topic => @topic)
end
context "A StudentExam" do
setup do
@student_exam = Factory(:student_exam, :exam => @exam, :student => @student, :room_number => "WB 302")
end
should "take place at 'Some School'" do
assert_equal @student_exam, 'Some School'
end
should "be in_progress? when created" do
assert @student_exam.in_progress?
end
should "not be in_progress? when finish! is called" do
@student_exam.finish!
assert !@student_exam.in_progress
end
end
end
Lösung
Wenn das Problem, diese Datensätze nur einmal schafft, können Sie eine Klassenvariable verwenden. Es ist kein sauberer Ansatz, aber zumindest sollte es funktionieren.
# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
should_belong_to :student
should_belong_to :exam
# These objects need to be created before we can create a StudentExam. Tests will NOT modify these objects.
# @school is a very time-expensive model to create (associations, external API calls, etc).
# We need a way to create the @school *ONCE* -- there's no need to recreate it for every single test.
@@school = Factory(:school)
@@student = Factory(:student, :school => @@school)
@@topic = Factory(:topic, :school => @@school)
@@exam = Factory(:exam, :topic => @@topic)
context "A StudentExam" do
setup do
@student_exam = Factory(:student_exam, :exam => @@exam, :student => @@student, :room_number => "WB 302")
end
should "take place at 'Some School'" do
assert_equal @student_exam, 'Some School'
end
should "be in_progress? when created" do
assert @student_exam.in_progress?
end
should "not be in_progress? when finish! is called" do
@@student_exam.finish!
assert !@student_exam.in_progress
end
end
end
EDIT: Um die Super-hässliche Abhilfe fixiert die Auswertung mit einer Instanzmethode verschieben
.# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
...
private
def school
@@school ||= Factory(:school)
end
# use school instead of @@school
def student
@@school ||= Factory(:student, :school => school)
end
end
Andere Tipps
Welche Art von Tests versuchen Sie zu schreiben? Wenn Sie tatsächlich sicherstellen möchten, dass alle diese Objekte entsprechend koordinieren, gerade schreiben Sie einen Integrationstest und die Geschwindigkeit ist nicht das primäre Anliegen. Wenn Sie jedoch zu Unit-Test des Modells sind versuchen, könnten Sie bessere Ergebnisse erzielen, indem aggressiv Anstoßen.
Zum Beispiel, wenn Sie zu überprüfen sind versuchen, dass eine Prüfung den Namen seines Schulverein verwendet, wenn Sie exam.location nennen (oder was auch immer Sie es sind anrufen), brauchen Sie nicht eine ganze Schule Objekt. Sie müssen nur sicherstellen, dass Prüfung die richtige Methode auf Schule ruft. Um zu testen, dass, könnten Sie so etwas wie die folgenden tun (mit Test :: Unit und Mokka, weil das ist, was ich kenne):
test "exam gets location from school name" do
school = stub_everything
school.expects(:name).returns(:a_school_name)
exam = Factory(:exam, :school => school)
assert_equal :a_school_name, exam.location
end
Grundsätzlich, wenn Sie Ihre Unit-Tests beschleunigen müssen, da Objekte zu teuer sind, zu konstruieren, sind Sie nicht wirklich Unit-Tests. Alle Testfälle oben fühlen, wie sie bei dem Unit-Test-Niveau, so dass Stummel Stummel Stummel!
sein solltenhttp: // m .onkey.org / 2009/9/20 / Make-your-shoulda-Tests-schneller-mit-fast_context ist ein ausgezeichneter Beitrag darüber, wie Ihre shoulda / factory-Mädchen Tests schneller zu machen, ein Juwel namens fast_context mit . Lassen Sie uns wissen, wenn sie nicht das, was Sie brauchen.
Es gibt ein Plugin namens fast_context ( github Link ), die kombiniert sollten Anweisungen in einem einzigen Zusammenhang, die Tests zu beschleunigen .
Die andere Sache, die ich verwendet habe meine Tests zu beschleunigen, ist bereits bevölkert die Fixturedaten. Factory ist langsam, weil es die Datensätze jedes Mal, wenn der Setup-Block läuft zu schaffen.
Ich schrieb ein Plugin namens Fixie , die Activerecord verwendet, um die Testdatenbank vorab zu füllen, so dass die Datensätze, die Sie für Ihre Tests müssen bereits erstellt. Sie können Fixie benutzen zusammen mit factory wenn Sie neue Datensätze zur Laufzeit erstellt werden sollen, auch.