シュダ + ファクトリーガール:テストを速くすることはできますか?
-
13-09-2019 - |
質問
スピードを上げる方法を探しています すべきだ + 工場の女の子 テスト。
テストしようとしているモデル (StudentExam
) は他のモデルとの関連性があります。これらの関連オブジェクトは、作成する前に存在する必要があります。 StudentExam
. 。そのため、それらは次のように作成されます。 setup
.
ただし、当社のモデルの 1 つ (School
) の作成にはかなりの時間がかかります。なぜなら setup
毎回の前に呼び出されます should
ステートメントを実行すると、テスト ケース全体の実行に長い時間がかかります。新しいテスト ケースが作成されます。 @school
, @student
, @topic
そして @exam
実行されるすべての should ステートメントに対して。
これらのオブジェクトを一度に作成する方法を探しています そして一度だけ. 。のようなものはありますか? startup
のために before_all
残りのテスト ケース全体で保持されるレコードを作成できるメソッドは何ですか?
基本的に私はRSpecのものとまったく同じものを探しています 前(:すべて). 。これらのテストでは高価なオブジェクトは決して変更されないため、依存関係の問題については心配していません。
以下にテストケースの例を示します。長いコードについては申し訳ありません(私も作成しました) 要旨):
# 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
解決
これらのレコードを 1 回しか作成しないことが問題である場合は、クラス変数を使用できます。これはきれいなアプローチではありませんが、少なくとも機能するはずです。
# 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
編集:この非常に醜い回避策を修正するには、インスタンス メソッドを使用して評価を延期します。
# 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
他のヒント
どのような種類のテストを書こうとしていますか?これらすべてのオブジェクトが適切に調整されていることを実際に確認したい場合は、統合テストを作成することになり、速度は最大の関心事ではありません。ただし、モデルを単体テストしようとしている場合は、積極的にスタブ化することでより良い結果が得られる可能性があります。
たとえば、example.location (または任意の名前) を呼び出すときに、試験でその学校協会の名前が使用されていることを確認したい場合、学校オブジェクト全体は必要ありません。試験が学校での正しい方法を示しているかどうかを確認する必要があるだけです。それをテストするには、次のようなことを実行できます (私がよく知っているので、Test::Unit と Mocha を使用します)。
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
基本的に、オブジェクトの構築にコストがかかりすぎるために単体テストを高速化する必要がある場合、それは実際には単体テストではありません。上記のテスト ケースはすべて単体テスト レベルであるべきだと思われるため、スタブ スタブ スタブ!
http://m.onkey.org/2009/9/20/make-your-Shoulda-tests-faster-with-fast_context は、fast_context と呼ばれる gem を使用して、 shoulda/factory-girl テストを高速化する方法についての優れた投稿です。必要なものでない場合はお知らせください。
というプラグインがあります 高速コンテキスト (ギットハブのリンク) should ステートメントを 1 つのコンテキストに結合し、テストを高速化します。
テストを高速化するために私が使用しているもう 1 つの方法は、フィクスチャ データの事前入力です。FactoryGirl は、セットアップ ブロックが実行されるたびにこれらのレコードを作成するため、処理が遅くなります。
というプラグインを書きました ピスト これは、ActiveRecord を使用してテスト データベースに事前に入力するため、テストに必要なレコードがすでに作成されています。実行時に新しいレコードを作成したい場合は、Fixie を FactoryGirl と一緒に使用することもできます。