我正在寻找一种方法来加快我的Shoulda + FactoryGirl测试。
我正在尝试测试的模型(StudentExam
)与其他模型有关联。在创建StudentExam
之前,这些关联对象必须存在。因此,它们是在setup
。
但是,我们的一个模型(School
)需要很长时间才能创建。因为setup
在每个should
语句之前被调用,所以整个测试用例需要很长时间才能执行 - 它会创建一个新的@school
,@student
,@topic
和{ {1}}为每个执行的语句执行。
我正在寻找一种方法来创建这些对象一次,只有一次。对@exam
方法有startup
之类的东西可以让我创建记录,这些记录会在测试用例的其余部分中持续存在吗?
基本上我正在寻找与RSpec before(:all)完全相同的东西。我不关心依赖的问题,因为这些测试永远不会修改那些昂贵的对象。
这是一个示例测试用例。为长代码道歉(我还创建了一个gist):
before_all
答案 0 :(得分:2)
如果问题是仅创建一次这些记录,则可以使用类变量。 这不是一个干净的方法,但至少它应该工作。
# 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
答案 1 :(得分:2)
你想写什么样的考试?如果您确实希望确保所有这些对象都正确协调,那么您正在编写集成测试,并且速度不是您主要关注的问题。但是,如果您正在尝试对模型进行单元测试,则可以通过积极的方式实现更好的结果。
例如,如果您在调用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
基本上,如果你需要加速你的单元测试,因为对象太昂贵而无法构建,你就不是真正的单元测试。上面的所有测试用例都感觉它们应该处于单元测试级别,所以存根存根存根!
答案 2 :(得分:0)
http://m.onkey.org/2009/9/20/make-your-shoulda-tests-faster-with-fast_context是一篇很棒的文章,关于如何使用名为fast_context的gem更快地进行你的女孩/工厂女孩测试。如果它不是你需要的,请告诉我。
答案 3 :(得分:0)
有一个名为fast_context(github link)的插件,它将should语句组合到一个上下文中,从而加速了测试。
我用来加速测试的另一件事就是预先填充灯具数据。 FactoryGirl很慢,因为每次设置块运行时它都会创建这些记录。
我编写了一个名为Fixie的插件,它使用ActiveRecord预先填充测试数据库,因此您已经创建了测试所需的记录。如果要在运行时创建新记录,可以将Fixie与FactoryGirl一起使用。