Должна + FactoryGirl:Могу ли я сделать тесты быстрее?
-
13-09-2019 - |
Вопрос
Я ищу способ ускорить свой Должен ли + ФабрикаДевушка тесты.
Модель, которую я пытаюсь протестировать (StudentExam
) имеет ассоциации с другими моделями.Эти связанные объекты должны существовать, прежде чем я смогу создать StudentExam
.По этой причине они создаются в setup
.
Однако одна из наших моделей (School
) требует значительного времени для создания.Потому что setup
звонят перед каждым should
оператор, выполнение всего тестового примера занимает целую вечность — он создает новый @school
, @student
, @topic
и @exam
для каждого выполненного оператора must.
Я ищу способ создать эти объекты один раз и только один раз.Есть ли что-то вроде 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
Решение
Если проблема заключается в создании этих записей только один раз, вы можете использовать переменную класса.Это не совсем чистый подход, но, по крайней мере, он должен работать.
# 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
Другие советы
Какие тесты вы пытаетесь написать?Если вы действительно хотите убедиться, что все эти объекты координируются должным образом, вы пишете интеграционный тест, и скорость не является вашей главной заботой.Однако, если вы пытаетесь выполнить модульное тестирование модели, вы можете добиться лучших результатов, если будете агрессивно заглушать.
Например, если вы пытаетесь проверить, что экзамен использует имя ассоциации школы, когда вы вызываетеexam.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 — отличный пост о том, как ускорить тестирование musta/factory-girl с помощью драгоценного камня под названием fast_context.Дайте мне знать, если это не то, что вам нужно.
Есть плагин под названием быстрый_контекст (ссылка на гитхаб), который объединяет операторы must в единый контекст, ускоряя тесты.
Еще одна вещь, которую я использую для ускорения своих тестов, — это предварительное заполнение данных о приспособлениях.FactoryGirl работает медленно, поскольку создает эти записи каждый раз при запуске блока настройки.
Я написал плагин под названием Фиксики который использует ActiveRecord для предварительного заполнения тестовой базы данных, поэтому записи, необходимые для ваших тестов, уже созданы.Вы можете использовать Fixie вместе с FactoryGirl, если хотите также создавать новые записи во время выполнения.