Shoulda + FactoryGirl: Posso fazer meus testes mais rápido?
-
13-09-2019 - |
Pergunta
Eu estou procurando uma maneira de acelerar o meu Shoulda + noreferrer testes FactoryGirl .
O modelo que eu estou tentando teste (StudentExam
) tem associações com outros modelos. Esses objetos associados deve existir antes que eu possa criar um StudentExam
. Por essa razão, eles são criados em setup
.
No entanto, um dos nossos modelos (School
) leva tempo significativo para criar. Porque setup
é chamado antes de cada declaração should
, todo o caso de teste leva eras para executar -. Ele cria um @school
novo, @student
, @topic
e @exam
para cada instrução executada deve
Eu estou procurando uma maneira de criar esses objetos uma vez e somente uma vez . Existe algo como um startup
para o método before_all
que me permitisse criar registros que persistem em todo o resto do caso de teste?
Basicamente, eu estou procurando algo exatamente como antes do RSpec (: all) . Eu não estou preocupado com a questão de dependências uma vez que esses testes nunca vai modificar esses objetos caros.
Aqui está um exemplo de caso de teste. Desculpas para o longo código (Eu também criou um essência ):
# 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
Solução
Se o problema está criando esses registros apenas uma vez, você pode usar uma variável de classe. Não é uma abordagem limpa, mas pelo menos ele deve funcionar.
# 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:. Para corrigir a solução super-feio adiar a avaliação com um método de instância
# 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
Outras dicas
Que tipo de testes que você está tentando escrever? Se você realmente quer ter certeza de que todos esses objetos estão a coordenar de forma adequada, você está escrevendo um teste de integração e velocidade não é a sua principal preocupação. No entanto, se você está tentando unidade testar o modelo, você pode obter melhores resultados por arrancar de forma agressiva.
Por exemplo, se você está tentando verificar se um exame usa o nome de sua associação escola quando você chamar exam.location (ou o que você está chamando-lo), você não precisa de um objeto de toda a escola. Você só precisa ter certeza de que o exame está chamando o método certo na escola. Para testar isso, você poderia fazer algo como o seguinte (usando Test :: Unit e Mocha, porque é isso que eu estou familiarizado com):
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
Basicamente, se você precisa para acelerar os testes de unidade porque os objetos são muito caros para construir, você não é realmente o teste de unidade. Todos os casos de teste acima sentir como eles devem estar no nível de teste de unidade, de modo esboço esboço esboço!
http: // m .onkey.org / 2009/9/20 / make-your-shoulda-testes-rápido-com fast_context é um excelente post sobre como fazer seus testes shoulda / fábrica-menina mais rápido, usando uma jóia chamada fast_context . Deixe-me saber se não é o que você precisa.
Há um plugin chamado fast_context ( github ligação ) que combina devem declarações em um único contexto, acelerar os testes .
A outra coisa que eu tenho vindo a utilizar para acelerar meus testes é pré-preencher os dados do dispositivo elétrico. FactoryGirl é lento porque está criando os registros cada vez que o bloco de configuração é executado.
Eu escrevi um plugin chamado Fixie que usa ActiveRecord para pré-preencher o banco de dados de teste, de modo que o registra o que você precisa para seus testes já estão criadas. Você pode usar Fixie juntamente com FactoryGirl se você quiser criar novos registros em tempo de execução, também.